Promises
In Raku, Promises represent the simplest of the high-level language features for communicating between asynchronous tasks. They are very much like promises between people. For example, I might promise my son I will help him with his school work. I keep that promise when I help him. Or I break that promise if, for any reason, I fail to help him. The same is true of a Promise in Raku. A Promise to return a value is kept when that value arrives. A Promise to return a value is broken if an error occurs that prevents the value from arriving.
So, let’s see a basic promise in action in Raku:
my $promise = start {
my $nth = 0;
my $nth-prime;
for 1..* -> $number {
$nth++ if $number.is-prime;
if $nth == 10_000 {
$nth-prime = $number;
last;
}
}
$nth-prime;
}
await $promise.then({ say .result });
The code above uses a
start
block to begin finding the 10,000th prime. This block returns a
Promise
object. This object exists in
one of three states (which you can check with the .status
method). The initial
state is Planned
and then it enters either of two final states. The Broken
state is entered if a result cannot be reached (usually because an exception
occurred) and the Kept
state is entered when the result becomes available.
Once in the Kept
status, the .result
method will immediately return the
value of the kept Promise
.
Promises can be chained together using then .then
method. This works by adding
another block that starts as soon as the first is kept. The new block will be
given the previous Promise
object as an argument and the method returns a new
Promise
which will contain result from the following block.
The start
block in the code above schedules the computation to run on the next
unused thread in the default thread pool and returns a Promise
object.1 We use .then()
to output the .result
of the
computation as soon as it comes available.
Finally, we have the await
statement, which causes the main thread to pause until the value becomes
available. Without that statement, our program would end before the computation
completes.
An await
also allows a broken Promise to deliver exceptions. Consider this
code:
my $promise = start { die 'bad stuff' }
sleep 1;
say 'something';
The code above will sleep for 1 second and print “something” to output. However,
the exception will never be received. This is because while an exception causes
the promise to be broken, we aren’t looking at the result of the Promise at all.
We can add an await
on the Promise where we are ready to receive the value and
any exception thrown that causes the Promise to be broken will be received:
my $promise = start { die 'bad stuff' }
sleep 1;
say 'something';
await $promise;
CATCH {
default {
say "ERROR: $_.message()";
}
}
This code does exactly the same thing as before, but also outputs “ERROR: bad
stuff” after “something”. Always be sure to either handle your exceptions inside
the start
block or in another block that receives the Promise
this way, or
you may end up with bizarre and unexpected problems.
Those are the essential elements of Raku Promises.
Cheers.
-
Note that this is the process for the usual default scheduler running in Rakudo under MoarVM. What actually happens might vary somewhat based on the current value in the
$*SCHEDULER
variable. I will discuss this further in a future post. ↩︎