Cancelable promises proposal

Glen Huang curvedmark at gmail.com
Tue Aug 4 00:27:35 UTC 2015


@Andrea @Yad

The only thing I'm not sure about is this "some promises have the abort ability, some don't" design. It might seem clear when you first create that promise, but it gets messy when you start to pass that promise around. It's very easy for some function to accidentally have the ability to abort() where it shouldn't.
 
> On Aug 4, 2015, at 1:19 AM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:
> 
> not so different from what I've proposed already, except it looks ugly with a `self` at the end, and it's based on runtime method attachment that could happen at any time or mislead, or forget to invoke reject etc etc ... I will try to stay away from this thread 'cause I've already talked as much as I could about this topic but my proposal was this one:
> 
> ```js
> var p = new Proomise((resolve, reject, fnIfAborted) => {
>   let timer = setTimeout(resolve, 3000, 'All Good');
> 
>   // **only** if fnIfAborted is invoked here
>   // the promise will be cancelable, otherwise it cannot
>   // be cancelable later on, and it's not possible
>   // to even invoke fnIfAborted later on or asynchronously
>   // this is the best I could think  to make the contract as simple as possible
>   fnIfAborted(function () {
>     clearTimeout(timer);
>   });
> });
> ```
> 
> Once established that the user that created the Promise is the only one capable of deciding if that could be cancelable or not, there's no way anything else could interfeer with that cancelability, and the promise can  be passed around as cancelable, or simply wrapped through another non cancelable promise.
> 
> This gives the promise creator the ability to cancel it without exposing such ability to the outer world.
> 
> The eventual `p.abort()` if executed after the promise has been resolved/rejected will **not** invoke the `fnIfAborted` internally passed callback.
> 
> Reasons such callback must be defined in the Promise scope is because only there there would be eventually the ability to resolve, reject, or abort the operation.
> 
> It's simple, it's probably a bit ugly, but it's there to solve edge cases ( basically all network related cases or every case that might take long time and the user, or even  the developer, might get border and would like to do something else instead of wasting resources )
> 
> 
> 
> 
> Best Regards
> 
> 
> 
> 
> 
> 
> On Mon, Aug 3, 2015 at 7:08 AM, Yad Smood <y.s.inside at gmail.com <mailto:y.s.inside at gmail.com>> wrote:
> I have thought about something similar before, and used it in one of my libs. Recently, I have come up another idea, it was already implemented on Yaku:
> 
> let Promise = require('yaku')
> 
> // The `self` is the instance of the newly created promise.
> let p = new Promise((resolve, reject, self) => {
>     let tmr = setTimeout(resolve, 3000)
> 
>     let self.abort = () => {
>         clearTimeout(tmr)
>         reject(new Error('abort promise'))
>     }
> 
> })
> 
> p.abort()
> The origin post is here: https://github.com/promises-aplus/cancellation-spec/issues/16 <https://github.com/promises-aplus/cancellation-spec/issues/16>.
> 
> what do you think about it?
> 
> 2015年8月3日(月) 8:43 Glen Huang <curvedmark at gmail.com <mailto:curvedmark at gmail.com>>:
> 
> I was discussing with Jake Archibald about cancelable promises the other day. I was trying to convince him promise.cancel() might not be a good idea. But that conversation unfortunately leans more towards on how the abort API should be exposed on fetch. I did provide some thoughts on that topic, and think the abort API represents a strong use case for cancelable promises. But I feel what's more important is the underlying control flow design.
> 
> So I would like to offer my proposal of cancelable promises here, and would like to ask if you think it's a good idea, or if promise.cancel() is actually a good idea and my understanding of promises is flawed.
> 
> I think that promises should be an observation API. In other words, it should be kept as one-way communication. This keeps promises simple and easy to reason about.
> 
> When we talk about cancelable promises, what we really want is the ability to:
> 
> 1. abort the action that the root promise observes.
> 2. let child promises show disinterest on the result of that action
> 
> With the premise that promises should be one-way communication, it's clear that #1 should be achieved with a separate API. For example:
> 
> ```
> let query = queryDB(sql);
> query.done.then(data => console.log(data));
> query.abort();
> ```
> 
> This means you need to have access to that separate API in order to abort the action, instead of just the promise.
> 
> And abort() rejects the root promise with a special error object, if it's pending.
> 
> This also means the abort API doesn't have to be universal. Each action initiator can design their own APIs to abort that action.
> 
> And correspondingly, to create a "cancelable promise" with the Promise constructor, it can be as simple as:
> 
> ```
> function doAction() {
>         let abort;
>         let done = new Promise((res, rej) => {
>                 asyncAction(res, rej);
>                 abort = () => rej(new AbortError());
>         });
>         return {done, abort};
> }
> ```
> 
> For #2, I propose we add a method to Promise.prototype that undos .then() (probably also a sugar to undo .catch()) like removeEventListener undos addEventListener. For example.
> 
> ```
> let query = queryDB(sql);
> let updateView = data => render(data);
> let log = data => console.log(data);
> query.done.then(updateView).then(log);
> query.done.ignore(updateView); // deregister callback, updateView and log will never be called
> setTimeout(() => {
>         query.done.then(updateView); // unless callback is registered again
> }, timeEnoughForQueryToFinish);
> ```
> 
> You can think it as that each promise keeps a list of its child promises, when the same callback is passed to .ignore() it sets a flag on the corresponding child promise so that when itself resolves/rejects, it won't pass that state to that child promise, unless that the same callback is later registered again.
> 
> What do you think of this design? Do you think it covers all of your use cases for cancelable promises?
> 
> I have to give credit for Kyle Simpson and Jake Archibald for this idea. Kyle's opposition of sending signal back to the promise vendor, and Jake's argument that we need a way to let an observer signal disinterest greatly clarifies my understanding of promises.
> 
> I guess someone has probably expressed this idea somewhere in some way (or in some libraries). Sorry if I missed that.
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>
> https://mail.mozilla.org/listinfo/es-discuss <https://mail.mozilla.org/listinfo/es-discuss>
> 
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>
> https://mail.mozilla.org/listinfo/es-discuss <https://mail.mozilla.org/listinfo/es-discuss>
> 
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20150804/0f8c7a93/attachment-0001.html>


More information about the es-discuss mailing list