Cancelable promises proposal
Andrea Giammarchi
andrea.giammarchi at gmail.com
Tue Aug 4 05:32:41 UTC 2015
my proposal doesn't make abortability accidental, only promises that has
passed through their initialization the callback would be cancelable,and
this could be reflected through a `.cancelable` property. We could have
`.abort()` throwing if used when cancelable is true ( or not
undefined for backward compatiblity sake )
The only problem I see with my proposal is thay purists would never accept
cancel-ability in first place, and pragmatists would never find out what
should be the behavior once aborted 'cause Promise can only reject and
never be ignored.
So the TL;DR is that I don't see cancel-able promises becoming reality at
any point in these days, I rather would think from scratch something better
( Task ? ) and leave Promises the way these are, hoping standard bodies
will just stop putting/using them for everything asynchronous that might
take more than 10ms and **should** be cancelable.
I'm the first one that want them cancelable, and at this point, after
months of discussions, the one that also doesn't see that as a solution.
Best Regards
On Tue, Aug 4, 2015 at 1:27 AM, Glen Huang <curvedmark at gmail.com> wrote:
> @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> 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.
>>
>> what do you think about it?
>>
>> 2015年8月3日(月) 8:43 Glen Huang <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
>>> 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/20150804/176c1748/attachment.html>
More information about the es-discuss
mailing list