>From experience, I'm very much in favor of the cancellation token.  Though
promises provide a good inspiration for cancellation, they don't quite fit
the problem directly.

The approach has been explored, though the details are not published. I
implemented cancellation for the Midori system. Midori was an incubation
project to build a new OS, applications, etc. in a safe language (a C#
variant) using lightweight processes communicating via promises (easily 2M+
lines of C#). Among other things, it strongly influenced the C# async
support. See http://joeduffyblog.com/2015/11/19/asynchronous-everything/
for more info. After looking at various options, the approach developed for
.Net covered all the requirements we had and could be adapted surprisingly
well to the async world of promises and Midori. There were some important
differences though.

In a concurrent, async, or distributed system, cancellation is
*necessarily *asynchronous: there's fundamentally a race between a
computation "expanding" (spawning more work) and cancellation running it
down. But as you note, a cancellation system must support the ability to
*synchronously *and cheaply test whether a computation is being cancelled.
That allows one for example to write a loop over thousands of records that
will abort quickly when cancelled without scheduling activity for the
remaining records. (Because of the inherent race in cancellation, I named
that "isCancelling" rather than "isCancelled" to be a small reminder that
computation elsewhere might not yet have heard that it should stop.)

In async cancellation, the promise "then" seems like it could support the
asycn "cleanup" action on cancellation. However there are a lot of
cancellation use cases in which the appropriate cleanup action changes as a
computation proceeds. For those, using "then" is not adequate. For example,
a browser would have a cancellation token associated with a page load. Thus
the same token is used during parsing, retrieving secondary images, layout,
etc. If the user hits "stop", the token is cancelled, and so all the
various heterogeneous page rendering activities are cancelled. But the
cleanup action to "close the socket that you are retrieving an image over"
becomes expensive deadweight once the image has been retrieved. On a page
that loads 100 images four at a time, you would want 4 cleanup actions
registered, not 100.

For that and other reasons, we found it much clearer to give
cancellationToken it's own type. That also allows convenient patterns to be
directly supported, such as:

    async function f(cancel) {
      await cheapOperation(cancel);
      cancel.*throwIfCancelling*(); // throws if the token is cancelling
      await expensiveOperation(cancel);
    }

Note that the above would more likely have been written simply:

    async function f(cancel) {
      await cheapOperation(cancel);
      await expensiveOperation(cancel);
    }

The reason is that if the cheapOperation was aborted, the first await would
throw (assuming cheapOperation terminates abruptly or returns a broken
promise). If it got past cheapOperation, we already know that
expensiveOperation is going to be smart enough to cancel, so why clutter
our world with redundant aborts?  e.g.,

    async function expensiveOperation(cancel) {
      while (hasFramesToRender() && !cancel.isCancelling()) {
          await renderFrame(this.nextFrame(), cancel);
      }
    }

Thus, using cancellation tokens becomes simpler as cancellation becomes
more pervasive in the libraries. Typically, if an operation takes a
cancellation token as an argument, then you don't need to bother protecting
it from cancellation. As a result, explicit cancellation handling tends to
only be needed in lower level library implementation, and client code
passes their available token to either operations or to the creation of
objects (e.g., pass in your token when you open a file rather than on every
file operation).



On Mon, Jan 4, 2016 at 7:46 AM, Kevin Smith <zenpars...@gmail.com> wrote:

> I'm interested in exploring the idea of using an approach similar to
> .NET's cancelation tokens in JS for async task cancelation.  Since the
> cancelation "flag" is effectively an eventual value, it seems like promises
> are well-suited to modeling the token.  Using a promise for a cancelation
> token would have the added benefit that the eventual result of any
> arbitrary async operation could be used as a cancelation token.
>
> First, has this idea been fully explored somewhere already?  We've
> discussed this idea on es-discuss in the past, but I don't remember any
> in-depth analysis.
>
> Second, it occurs to me that the current promise API isn't quite ideal for
> cancelation tokens, since we don't have synchronous inspection
> capabilities.  For example, suppose that we have this async function:
>
>     async function f(cancel) {
>       let canceled = false;
>       cancel.then(_=> canceled = true);
>       await cheapOperation(cancel);
>       if (canceled) throw new CanceledOperationError();
>       await expensiveOperation(cancel);
>     }
>
> Now, when the `canceled` flag is checked before `expensiveOperation`, it
> may be the case that the `cancel` promise has already been resolved (i.e.
> [[PromiseState]] is "fulfilled"), but the `then` callback has not executed
> yet.  In such a case `expensiveOperation` would be started, even though a
> cancelation has been requested.
>
> For this reason, it seems like some kind of synchronous inspection ability
> is required in order to use promises as cancelation tokens.  With
> synchronous inspection, the above function would be written something like:
>
>     async function f(cancel) {
>       await cheapOperation(cancel);
>       if (cancel.isFulfilled()) throw new CanceledOperationError();
>       await expensiveOperation(cancel);
>     }
>
> Without synchronous inspection, I think we'd need to introduce some new
> API for cancelation tokens.
>
> Thoughts?  (For the purposes of this discussion, let's avoid discussing
> the relative merits of cancelable promises vs. cancelation tokens.)
>
>
> _______________________________________________
> es-discuss mailing list
> es-discuss@mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
>
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to