Skip to content

Commit

Permalink
Implement methods andthen and orelse on Promise
Browse files Browse the repository at this point in the history
The methods are intended to simplify conditional processing and chaining
of kept or broken promises when used instead of the `then` method.

`andthen` is fired only when its invocant promise is kept. If the
invocant is broken then its cause becomes the cause of the promise
returned by `andthen`:

`orelse` is fired if its invocant is broken. If the invocant is kept
then the promise returned by `orelse` is kept with invocant's result:

If the invocant of `orelse` is broken the purpose of the method is to
intercept the cause, handle it, and return a value to be used as the
result of `orelse`-returned promise:
  • Loading branch information
vrurg committed Jul 16, 2023
1 parent 34a1a5d commit acd8cc4
Showing 1 changed file with 55 additions and 11 deletions.
66 changes: 55 additions & 11 deletions src/core.c/Promise.pm6
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,17 @@ my class Promise does Awaitable {
}
}

method !PLANNED-THEN(\then-promise, \vow, \then-code) {
# Push 2 entries to $!thens: something that starts the then code,
# and something that handles its exceptions. They will be sent to the
# scheduler when this promise is kept or broken.
nqp::bindattr(then-promise, Promise, '$!dynamic_context', nqp::ctx());
nqp::push(nqp::ifnull($!thens, ($!thens := nqp::list)), then-code);
nqp::push($!thens, -> $ex { vow.break($ex) });
nqp::unlock($!lock);
then-promise
}

method then(Promise:D: &code) {
nqp::lock($!lock);
if $!status == Broken || $!status == Kept {
Expand All @@ -212,20 +223,53 @@ my class Promise does Awaitable {
self.WHAT.start( { code(self) }, :$!scheduler);
}
else {
# Create a Promise, and push 2 entries to $!thens: something that
# starts the then code, and something that handles its exceptions.
# They will be sent to the scheduler when this promise is kept or
# broken.
my $then-p := self.new(:$!scheduler);
nqp::bindattr($then-p, Promise, '$!dynamic_context', nqp::ctx());
my $vow := $then-p.vow;
nqp::push(
nqp::ifnull($!thens,($!thens := nqp::list)),
{ my $*PROMISE := $then-p; $vow.keep(code(self)) }
);
nqp::push($!thens, -> $ex { $vow.break($ex) });
self!PLANNED-THEN($then-p, $vow, { my $*PROMISE := $then-p; $vow.keep(code(self)) } )
}
}

method andthen(Promise:D: &code) {
nqp::lock($!lock);
if $!status == Broken {
nqp::unlock($!lock);
self.WHAT.broken($!result)
}
elsif $!status == Kept {
# Already have the result, start immediately.
nqp::unlock($!lock);
self.WHAT.start( { code(self) }, :$!scheduler);
}
else {
my $then-p := self.new(:$!scheduler);
my $vow := $then-p.vow;
self!PLANNED-THEN( $then-p,
$vow,
{ $!status == Kept
?? do { my $*PROMISE := $then-p; $vow.keep(code(self)) }
!! $vow.break($!result) })
}
}

method orelse(Promise:D: &code) {
nqp::lock($!lock);
if $!status == Broken {
nqp::unlock($!lock);
self.WHAT.start( { code(self) }, :$!scheduler);
}
elsif $!status == Kept {
# Already have the result, start immediately.
nqp::unlock($!lock);
$then-p
self.WHAT.kept($!result);
}
else {
my $then-p := self.new(:$!scheduler);
my $vow := $then-p.vow;
self!PLANNED-THEN( $then-p,
$vow,
{ $!status == Kept
?? $vow.keep($!result)
!! do { my $*PROMISE := $then-p; $vow.keep(code(self)) } })
}
}

Expand Down

0 comments on commit acd8cc4

Please sign in to comment.