raku.gg / concurrency

Supplies and React

2026-03-31

While Promises represent single future values and Channels are message queues, Supplies are Raku's solution for streams of asynchronous events. Combined with the react/whenever syntax, they provide a reactive programming model built right into the language.

What Is a Supply?

A Supply is an asynchronous stream of values that you can tap into. Think of it like an event emitter or an observable:
my $supply = Supply.interval(1); # Emits 0, 1, 2, ... every second my $tap = $supply.tap( -> $v { say "Got: $v" }, done => { say "Done!" }, quit => { say "Error: {.message}" }, ); sleep 5; $tap.close;
Supply.interval(1) creates a live supply that emits an incrementing integer every second.

Live vs On-Demand Supplies

Raku has two kinds of supplies:

Live supplies emit values regardless of whether anyone is listening. If you tap in late, you miss earlier values:

my $supplier = Supplier.new; my $supply = $supplier.Supply; # This is a live supply $supplier.emit("first"); # No one listening, lost $supply.tap(-> $v { say "A: $v" }); $supplier.emit("second"); # Tap A sees this $supply.tap(-> $v { say "B: $v" }); $supplier.emit("third"); # Both A and B see this $supplier.done;
On-demand supplies replay their sequence for each new subscriber:
my $on-demand = supply { emit 1; emit 2; emit 3; }; $on-demand.tap(-> $v { say "First subscriber: $v" }); $on-demand.tap(-> $v { say "Second subscriber: $v" }); # Both get 1, 2, 3

The Supplier Pattern

Supplier is the write end. Supply is the read end. This separation keeps things clean:
my $supplier = Supplier.new; my $supply = $supplier.Supply; # Consumer side $supply.tap(-> $msg { say "Received: $msg" }); # Producer side $supplier.emit("hello"); $supplier.emit("world"); $supplier.done; # Signal completion

react/whenever

The react block combined with whenever gives you structured reactive code:
my $supplier = Supplier.new; start { react { whenever $supplier.Supply -> $value { say "Got: $value"; } } say "React block finished"; }; $supplier.emit(1); $supplier.emit(2); $supplier.emit(3); $supplier.done; # This causes the react block to finish sleep 1;
The react block stays alive until all its whenever sources are done or the block is explicitly left.

Multiple Whenever Clauses

A single react block can listen to multiple supplies:
my $numbers = Supplier.new; my $letters = Supplier.new; start { react { whenever $numbers.Supply -> $n { say "Number: $n"; } whenever $letters.Supply -> $l { say "Letter: $l"; } } }; $numbers.emit(1); $letters.emit('a'); $numbers.emit(2); $letters.emit('b'); $numbers.done; $letters.done; sleep 1;

Supply Combinators

Supplies support powerful transformation operations:
my $supplier = Supplier.new; my $supply = $supplier.Supply; # Grep -- filter values my $errors = $supply.grep(*.starts-with('ERROR')); # Map -- transform values my $lengths = $supply.map(*.chars); # Unique -- deduplicate my $unique = $supply.unique; # Batch -- collect values into groups my $batched = $supply.batch(elems => 3); $errors.tap(-> $v { say "Error: $v" }); $lengths.tap(-> $v { say "Length: $v" }); $supplier.emit("INFO: all good"); $supplier.emit("ERROR: disk full"); $supplier.emit("ERROR: network down"); $supplier.done; sleep 1;

Throttling and Timing

my $supplier = Supplier.new; my $supply = $supplier.Supply; # Stable -- wait for a pause in emissions my $debounced = $supply.stable(0.5); # 500ms debounce $debounced.tap(-> $v { say "Stable: $v" }); # Rapid-fire emissions $supplier.emit("a"); $supplier.emit("b"); $supplier.emit("c"); # Only "c" will be emitted after 500ms pause sleep 2; $supplier.done;

Merging Supplies

Combine multiple supplies into one:
my $s1 = Supplier.new; my $s2 = Supplier.new; my $merged = Supply.merge($s1.Supply, $s2.Supply); $merged.tap(-> $v { say "Merged: $v" }); $s1.emit("from s1"); $s2.emit("from s2"); $s1.emit("also s1"); $s1.done; $s2.done; sleep 1;

Supply.interval for Timers

Create periodic events:
react { # Emit every 2 seconds whenever Supply.interval(2) -> $tick { say "Tick $tick at {DateTime.now}"; done if $tick >;= 4; # Stop after 5 ticks } } say "Timer done";

Practical Example: File Watcher

# Watch a directory for changes sub watch-dir(IO::Path $dir) { supply { whenever Supply.interval(1) { state %seen; for $dir.dir -> $file { my $mod = $file.modified; if !%seen{$file} || %seen{$file} != $mod { %seen{$file} = $mod; emit "$file modified at $mod"; } } } } } # Usage: # react { # whenever watch-dir("/tmp".IO) -> $event { # say $event; # } # }

Practical Example: Event Bus

class EventBus { has %!suppliers; method on(Str $event --> Supply) { %!suppliers{$event} //= Supplier.new; %!suppliers{$event}.Supply } method emit(Str $event, $data) { if %!suppliers{$event} { %!suppliers{$event}.emit($data); } } } my $bus = EventBus.new; $bus.on('user:login').tap(-> $user { say "Login: $user" }); $bus.on('user:logout').tap(-> $user { say "Logout: $user" }); $bus.on('user:login').tap(-> $user { say "Audit: $user logged in" }); $bus.emit('user:login', 'Alice'); $bus.emit('user:logout', 'Bob'); $bus.emit('user:login', 'Carol'); sleep 1;
Supplies and react/whenever give Raku a reactive programming model that feels natural and composable. They are the right choice for event streams, real-time data processing, and any situation where you need to respond to asynchronous events.