>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