{"atom":"regression/whenever-done-semantics","family":"regression","slug":"whenever-done-semantics","header":{"atom":"regression/whenever-done-semantics","kind":"whole-program — a minimal repro, not yet reduced to an atom","source":"curated"},"axes":null,"cols":null,"engines":["rakudo-2026.07","rakupp-3.14.0"],"kinds":["ref","pp"],"defFrom":"rakupp:t/regression/whenever-done-semantics.raku","counts":{"a":1,"f":0,"d":0,"c":0,"r":0,"n":0},"files":["atoms/regression/whenever-done-semantics.grid"],"records":[["0001","","# The `done` control exception + whenever-source coercions (found fixing\n# Supply.interval: the real ticker un-masked all of these). Passes on both\n# engines.\n#\n# - `done` EXITS the whenever block it is in (code after it must not run),\n# - `done` in a plain supply body ends the body right there,\n# - `whenever $supplier` (a raw Supplier) taps its .Supply — it must NOT run\n#   the block eagerly with the Supplier as topic,\n# - `whenever $promise` on an UNKEPT promise REGISTERS: the block fires once\n#   with the KEPT VALUE when it settles (the `whenever $kill { done }`\n#   shutdown idiom), not immediately with the promise object.\n\n# done exits the whenever block\nmy $x = 0;\nreact {\n    whenever Supply.from-list(1, 2, 3) {\n        done if $_ == 2;\n        $x++;\n    }\n}\ndie \"done did not exit the whenever block: x=$x\" unless $x == 1;\n\n# done ends a plain supply body\nmy @c;\nmy $s = supply { emit 1; done; emit 2; @c.push('body continued') };\n$s.tap({ @c.push($_) });\ndie \"done did not end the supply body: @c[]\" unless @c == 1 && @c[0] == 1;\n\n# whenever over a raw Supplier taps its .Supply\nmy $trigger = Supplier.new;\nmy $seen;\nmy $sup = supply {\n    whenever $trigger -> $v { $seen = $v; done; }\n}\nmy $done-p = Promise.new;\n$sup.tap({ ; }, done => { $done-p.keep(True) });\n$trigger.emit('payload');\nawait $done-p;\ndie \"supplier whenever saw '{$seen // '(nothing)'}'\" unless $seen eq 'payload';\n\n# whenever over an unkept Promise fires ONCE with the kept value\nmy $p = Promise.new;\nmy $got;\nmy $keeper = start { sleep 0.1; $p.keep(42) };\nreact {\n    whenever $p -> $v { $got = $v; done; }\n}\ndie \"promise whenever got {$got.raku}\" unless $got == 42;\n\n# the standard shutdown idiom: the react stays alive until the kill promise\nmy $kill = Promise.new;\nmy $ticks = 0;\nmy $killer = start { sleep 0.35; $kill.keep };\nreact {\n    whenever Supply.interval(0.05) { $ticks++ }\n    whenever $kill { done }\n}\ndie \"react died early: $ticks ticks\" if $ticks < 2;\n\nsay \"PASS\";","output PASS","a",["PASS","PASS"],0,[["budget","20s"]]]]}