raku.gg / concurrency

Promises and start

2026-03-19

Raku has first-class concurrency support built into the language. At the foundation of it all is the Promise, a value that will be resolved at some point in the future. Combined with start blocks and await, you can write concurrent code that reads almost like sequential code.

Creating Promises with start

The simplest way to create a promise is with a start block. This schedules code to run on a thread pool and immediately returns a Promise object:
my $promise = start { sleep 2; 42 }; say $promise.status; # Planned say await $promise; # 42 (waits for completion)
The start block runs asynchronously. The value of the last expression in the block becomes the result of the promise.

Promise States

A promise goes through a lifecycle:
my $p = start { sleep 1; "done" }; say $p.status; # Planned -- still running sleep 2; say $p.status; # Kept -- completed successfully say $p.result; # "done"
The three states are:
my $broken = start { die "something went wrong" }; sleep 1; say $broken.status; # Broken try { await $broken; CATCH { default { say "Caught: {.message}" } } }

Awaiting Multiple Promises

You can pass multiple promises to await and get all results at once:
my @promises = (1..5).map: -> $n { start { sleep $n * 0.5; $n ** 2 } }; my @results = await @promises; say @results; # [1 4 9 16 25]
All five computations run concurrently. await blocks until every promise is kept (or one breaks).

Chaining with .then

Use .then to schedule work that depends on a previous promise:
my $chain = start { 10 } .then({ .result * 2 }) .then({ .result + 5 }) .then({ say "Final: {.result}" }); await $chain; # Final: 25
Each .then block receives the previous promise as its argument. Call .result to get the value.

Promise.in -- Delayed Execution

Create a promise that is automatically kept after a delay:
my $delayed = Promise.in(3); say "Waiting..."; await $delayed; say "3 seconds have passed!";
This is useful for timeouts:
my $work = start { sleep 10; "done" }; my $timeout = Promise.in(2); my $winner = await Promise.anyof($work, $timeout); if $winner === $timeout { say "Timed out!"; } else { say "Completed: {$work.result}"; }

Promise.allof and Promise.anyof

These class methods let you combine promises:
# Wait for ALL promises to complete my @tasks = (1..3).map: -> $n { start { sleep $n; say "Task $n done" } }; await Promise.allof(@tasks); say "All tasks finished"; # Wait for the FIRST promise to complete my $fast = start { sleep 1; "fast" }; my $slow = start { sleep 5; "slow" }; await Promise.anyof($fast, $slow); say "First one done: {$fast.result}";

Keeping and Breaking Manually

You can create promises and resolve them manually using a Promise vow:
my $p = Promise.new; my $vow = $p.vow; start { sleep 1; $vow.keep("Here is your data"); }; say await $p; # "Here is your data"
To break a promise (signal an error):
my $p = Promise.new; my $vow = $p.vow; start { $vow.break("Connection failed"); }; sleep 1; try { await $p; CATCH { default { say "Error: {.message}" } } }

Practical Example: Parallel File Processing

Here is a realistic example that processes multiple files concurrently:
my @files = dir('.', test => /\.txt$/); my @promises = @files.map: -> $file { start { my $content = $file.slurp; my $words = $content.words.elems; "$file: $words words" } }; for await @promises -> $result { say $result; }

Error Handling Best Practices

Always handle potential errors when working with promises:
my @tasks = (1..5).map: -> $n { start { die "Bad number" if $n == 3; $n * 10 } }; for @tasks -> $task { try { say await $task; CATCH { default { say "Failed: {.message}" } } } }
Promises give you a clean, composable foundation for concurrency in Raku. In future posts, we will explore Channels and Supplies, which build on promises to handle streams of data.