I wrote this test program:
await Promise.anyof(
Promise.allof((^5).map: {start { sleep 10; say "done $_" } }),
Promise.in(5).then: { say 'ouch' }
);
sleep 10;
When the second promise times out it prints 'ouch' and the await exits, but the first promise's code block is still running. After five more seconds its five processes end and print 'done':
$ ./test1.p6
ouch
done 0
done 1
done 2
done 3
done 4
I tried to terminate the first promise assigning it to a variable and then calling the .break
method from the second promise, but it doesn't work.
Is there a way to kill the first promise and the other five sub-promises it started?
You can't cancel a Promise, but you can chain your promises with then, and reject at any point. new Promise((resolve, reject) => { // step 1 }) .then((result) => { if (!result) { // Reject the promise chain throw 'cancel'; } else { return ... // step 2 } }) .catch(err => { // cancelled });
Also keep in mind that if you want to break out of the chain in your error handler, it needs to return a rejected promise or throw an Error (which will be caught and wrapped in a rejected promise automatically). If you don't return a promise, then wraps the return value in a resolve promise for you.
With AbortController It is possible to use abort controller to reject promise or resolve on your demand: let controller = new AbortController(); let task = new Promise((resolve, reject) => { // some logic ...
If you don't return a promise, then wraps the return value in a resolve promise for you. This means that if you don't return anything, you are effectively returning a resolved promise for the value undefined.
You have to somehow tell the process that it doesn't have to finish.
my $cancel = Cancellation.new;
await Promise.anyof(
Promise.allof(
(^5).map: {
last if $cancel.cancelled;
start {
sleep 10;
say "done $_" unless $cancel.cancelled
}
}
),
Promise.in(5).then: {
$cancel.cancel;
say 'ouch'
}
);
If you want something like Promise.in()
that can be cancelled, let's start by looking at the existing code.
method in(Promise:U: $seconds, :$scheduler = $*SCHEDULER) {
my $p := self.new(:$scheduler);
my $vow := $p.vow;
$scheduler.cue({ $vow.keep(True) }, :in($seconds));
$p
}
Note that the result of $scheduler.cue
is a Cancellation.
I am just going to wrap a Promise, and a Cancellation in a class for simplicity.
(I don't want to reimplement every method).
class Cancellable-Timer {
has Promise $.Promise;
has $!vow;
has Cancellation $!cancel;
method !SET-SELF ( $!promise, $!vow, $!cancel ){
self
}
method in (::?CLASS:U: $seconds, :$scheduler = $*SCHEDULER) {
my $p := Promise.new(:$scheduler);
my $vow := $p.vow;
my $cancel = $scheduler.cue({ $vow.keep(True) }, :in($seconds));
self.bless!SET-SELF($p,$vow,$cancel);
}
method cancel ( --> Nil ) {
# potential concurrency problem
if $!Promise.status == Planned {
$!cancel.cancel; # cancel the timer
$!vow.break("cancelled"); # break the Promise
}
}
method cancelled () {
# Ignore any concurrency problems by using the Promise
# as the sole source of truth.
$!Promise.status ~~ PromiseStatus::Broken
}
}
my $timer = Cancellable-Timer.in(1);
my $say = $timer.Promise.then: *.say;
Promise.in(0.1).then: {$timer.cancel};
await $say;
Note that the above class is just a rough first draft.
Try with whenever:
$ perl6 -e '
react {
for ^5 -> $num {
whenever start { sleep 10 } {
say "done $num"
}
}
whenever Promise.in: 5 {
say "ouch";
done
}
}
' ouch
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With