Is it possible to terminate a promise's code block from another promise?

♀尐吖头ヾ 提交于 2020-04-12 09:31:11

问题


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?


回答1:


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.




回答2:


Try with whenever:

$ perl6 -e '

react {
   for ^5 -> $num {
      whenever start { sleep 10 } {
         say "done $num"
      }
   }
   whenever Promise.in: 5 {
      say "ouch";
      done
   }
}

' ouch


来源:https://stackoverflow.com/questions/52955919/is-it-possible-to-terminate-a-promises-code-block-from-another-promise

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!