[ES7+] Promise cancellation (was: Re: Promise-returning delay function)
# Andrea Giammarchi (11 years ago)
I've seen this too late ... but thank you, looking forward to read more in here.
Best
I've seen this too late ... but **thank you**, looking forward to read more in here. Best Regards On Tue, Oct 28, 2014 at 6:22 PM, Ron Buckton <rbuckton at chronicles.org> wrote: > The direction I've taken around Promise cancellation given the API surface > of the ES6 Promise has been to use a different object to handle > cancellation. This approach is similar to the Cooperative Cancelation model > used in .NET. The API for this looks something like the following (using > TypeScript declarations): > > ``` > declare class CancellationTokenSource { > constructor(...linkedTokens: CancellationToken[]); > state: string; > token: CancellationToken; > cancel(reason?: any): void; > cancelAfter(delay: number, reason?: any): void; > close(): void; > } > > declare class CancellationToken { > constructor(source: CancellationTokenSource); > static none: CancellationToken; > state: string; > reason: any; > throwIfCanceled(): void; > register(callback: (reason: any) => void): void; > unregister(callback: (reason: any) => void): void; > } > ``` > > There are several reasons I'm using this approach: > - Separates the concerns of cancellation (CTS/CancellationToken) from > asynchronous completion (Promise) > - Distinguishes the relationship between the producer and consumer of > cancellation and completion signals: > - A function that performs an asynchronous operation produces the > completion signal, while the caller receives the signal. > - The caller produces the cancellation signal, while the function that > performs the asynchronous operation receives the signal. > - A Promise guarantees all continuations are asynchronous, and will be > scheduled in a later turn. Cancellation signals must be completed > synchronously, lest the signal is scheduled after the Promise resolves. > - The producer of a cancellation signal maintains sole control over the > ability to signal cancellation. Consumers can merely listen for the signal. > - Cancellation signals only propagate up the chain to a Promise producer > who intentionally listens for the signal. > - Cancellation signals cannot be sent by downstream consumers of a Promise > API that does not expose its cancellation source. > > I've created a gist[1] with a rough reference implementation of > CTS/CancellationToken, along with examples of many of the above points. > > However, this is not truly "cooperative" cancellation, when compared to > the approach .NET takes. True Cooperative Cancellation would involve > Promise internals being aware of a CancellationToken, and having a > cancellation signal affect the state of the Promise automatically in some > meaningful way. > > Ron > > [1] https://gist.github.com/rbuckton/256c4e929f4a097e2c16 > > ________________________________________ > From: es-discuss <es-discuss-bounces at mozilla.org> on behalf of C. Scott > Ananian <ecmascript at cscott.net> > Sent: Tuesday, October 28, 2014 11:34 AM > To: Dean Landolt > Cc: Mark S. Miller; Domenic Denicola; Mark Miller; es-discuss at mozilla.org > list > Subject: Re: Promise-returning delay function > > Dean, your idea is even better as a `Promise` *subclass*. > > The subclassed cancellable promises could share a cancel flag, and > throw an exception (or resolve to a different value, whichever you > like better for the API) when the cancel flag is set. The `then` > method on `CancellablePromise` returns another `CancellablePromise` > (the spec is nicely written this way), and you use `Promise.resolve` > to cast to a standard `Promise` to end the cancellation chain at an > appropriate place. > > See the `Promise.bind` implementation in `prfun` for an example of > using subclasses in this way: > https://github.com/cscott/prfun/blob/master/lib/index.js#L518 > --scott > > ps. Of course, to be precise, your API would be a factory function > which makes a new subclass of `CancellablePromise` for each cancel > scope, since the Promise spec associates the state with the > constructor identity. (I had some small issues with the "new subclass > for each X" mechanism in an earlier attempt at creating MonadicPromise > using subclasses, but I think these were ultimately resolved in the > spec---and in that case I was creating a new subclass for each call to > `Promise.resolve`, which was a whole 'nuther kettle of fish.) > > > On Tue, Oct 28, 2014 at 11:21 AM, Dean Landolt <dean at deanlandolt.com> > wrote: > > On Tue, Oct 28, 2014 at 10:41 AM, Andrea Giammarchi > > <andrea.giammarchi at gmail.com> wrote: > >> > >> The moment you pass a promise you have no control on who's adding what > as > >> callback or errorback which means you have no control over a .reject() > or > >> over a .success() > >> > >> .cancel() would eventually keep both queues unresolved so that nothing > >> should happen at all: no error handling, no error happened, and no > actions > >> needed ... it's simply canceled. > >> > >> This is the same reason .fetch() API needs also events to work properly > >> (including `progress`) ... you "cannot" base as example typeahead on > >> promises right now 'cause it's a mess to handle since response order is > not > >> guaranteed but you need to handle the .cancel() case without telling the > >> user something else went wrong ... what you suggest, a `new Noop()` > where > >> `Noop.prototype = Object.create(Error.prototype)` ? > >> > >> And how do you instruct unknown surrounding code that an `if (err > >> instanceof Noop)` should be the first line of every errback ? > >> > >> This is the reason few devs cannot adopt current standard Promises. > >> > >> `.cancel()` as well as `.abort()` is **very** important when many > network > >> operations are in place and/or you want to switch to a different state > >> without waiting for the previous one to be fulfilled. > >> > >> `new Promise(function (resolve, reject, cancel) {});` is the dumbest > idea > >> I could have now beside `new Promise(function (resolve, reject) > >> {}).on('cancel', cancelback).then(whatever)` one > >> > >> Anyway, this would deserve IMO a thread a part but I hope this topic > will > >> be discussed at some point. > >> > >> Best Regards > > > > > > > > I agree w/ Scott re: regularity of flow control, and I'd be particularly > > uneasy if a `cancel` method on a promise had any impact on upstream > > handlers. But ISTM something like this could be made to work (even if it > > looks a bit silly): > > > > ```js > > var cancelled; > > var never = new Promise(function () {}); > > Promise.delay(100/*ms*/).then(function(value) { > > return cancelled ? never : value; > > }, function (error) { > > return cancelled ? never : throw error; > > }); > > function cancel() { cancel = true }; > > ``` > > > > This could be cleaned up into a cancellation helper to quash downstream > > promises (this kind of utility would be a good use case for an `always` > > method if one isn't yet spec'd). > > > > Though I do wonder what happens to promises like these that get dropped > on > > the floor? Will downstream handlers be GC'd? Is this specified in any > way? I > > imagine dev tools want to warn about these kinds of promises — it may be > > helpful to have a way to signal that a promise was intentionally quashed. > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141028/5264382c/attachment.html>
The direction I've taken around Promise cancellation given the API surface of the ES6 Promise has been to use a different object to handle cancellation. This approach is similar to the Cooperative Cancelation model used in .NET. The API for this looks something like the following (using TypeScript declarations):
There are several reasons I'm using this approach:
I've created a gist[1] with a rough reference implementation of CTS/CancellationToken, along with examples of many of the above points.
However, this is not truly "cooperative" cancellation, when compared to the approach .NET takes. True Cooperative Cancellation would involve Promise internals being aware of a CancellationToken, and having a cancellation signal affect the state of the Promise automatically in some meaningful way.
Ron
[1] gist.github.com/rbuckton/256c4e929f4a097e2c16