W3C home > Mailing lists > Public > public-script-coord@w3.org > January to March 2015

Re: Cancelable promises

From: Salvador de la Puente González <salva@unoyunodiez.com>
Date: Sat, 28 Feb 2015 06:43:31 +0100
Message-ID: <CA+tDh5Vh87UMiQFLMHtO5-0pU62LuDiBxjannwvxf6jGfLrR6Q@mail.gmail.com>
To: John Lenz <concavelenz@gmail.com>
Cc: es-discuss@mozilla.org, Andrea Giammarchi <andrea.giammarchi@gmail.com>, "public-script-coord@w3.org" <public-script-coord@w3.org>
What is cancelable is the value of the fetch() promise, not the fetch
itself. Extending the promise API is extending the concept of control flow
and I think this require a separated discussion. To make fetch() to be
cancelable should not affect promises at all.

TL;DR; I think the cancelable promise makes no much sense. A promise is an
abstraction for a control flow, it is not a value.

When you call a function f() returning a promise, f() is not returning a
valid image* value. It is only returning an abstraction saying that,
eventually, it will be "replaced" by the proper function or fail.

*image -> understood like in a mathematical context but allowing functions
to have side effects.

This is why we can jump from promise-based syntax to await/async syntax. In
the latter we are emulating the synchronous syntax in which there is only
to flows, the regular flow and the exception flow. The exception flow,
which is born from the need of checking runtime conditions that forces
emergency paths.

With this scheme, there is no way for reacting upon abortion / cancellation
unless you do something like:

try {
  var response = await fetch(url);
} ifAbort(reason) {
  // what if aborted...
}

This resembles a lot to a simple exception and so, there should be treated
like another rejection error.

But this does not end here.

If we extend Promises API we are adding new flows, we are changing the flow
definition. The need of a cancelable promise rising from fetch() is
coupling fetch()'s specific implementation with control flow. This is why
you ended with FetchPromise.

If you are going to allow fetch() to be cancelable, what I actually think
is the thing which is indeed to be cancelable is the promise value for the
fetch() returned promise.

All these members mentioned in the discussion such as .isCancelled,
..requestCancel() and .setCancelHandler() should be part of the promise's
value and not part of the promise itself.

var response = await fetch(); // it waits for the value of the promise
which represents a fetch in progress.
f.abort (); // this is what is abortable, not the flow itself.

Aborting the value will affect the way in which other members behaves and
those are implementation details. For instance, a successfully abort() will
cause .isAborted() to resolve in true and .json() to fail with an instance
of FetchAborted() reason.

This way you are not coupling specific implementations' needs with control
flow abstractions.

Notice that extending a flow abstraction is not bad at all but remember
that If you continue pushing for the cancelable Promise, you are actually
extending a control flow. So, IMHO, you should:

* Allow a third parameter for .then() to react upon the new flow path.
* Change the Promise implementation to accept a second parameter which is
the callback to be called when the flow is aborted.
* Extend try syntax to take into account the new path to support
await/async syntax**

**The need for an extension in try is another argument against. If you
think carefully, an .abort() method is like controlling the flow from
outside, in the await/async syntax is like controlling the "= await" part
of the syntax which is simply unsupported.

var response = await fetch(url);

You can not abort() with await syntax from outside, it is only abortable by
internal reasons. If it is only abortable by internal reasons, then it is
almost the same than reject or accept. Just look at the value you are
choosing for the abort result. If it is a valid response, you are
accepting. If not, you are rejecting.

If you think with cancelable promises you're allowing richer execution
models, think about Tasks as values of promises and use composition and not
inheritance because they are not the same.

Hope it helps.
El 28/02/2015 05:01, "John Lenz" <concavelenz@gmail.com> escribió:

>
>
> On Fri, Feb 27, 2015 at 7:49 PM, John Lenz <concavelenz@gmail.com> wrote:
>
>> Closure Library's promise implementation supports "cancel":
>>
>>
>> https://github.com/google/closure-library/blob/master/closure/goog/promise/promise.js#L502
>>
>> A promise is cancelled only if all the "child" promises are also
>> cancelled.
>>
>
> I did not say that correctly, a "parent" promise can be cancelled by a
> "child" it is the only child or all the children cancel.  A parent can
> alway cancel its children (to the children this is simply "reject").  It
> does add a significant amount of complexity to the promise implementation
> but it is for the most part contained there.
>
> I don't believe that "cancel" can be added after the fact and an
> alternative (subclass or otherwise) is needed.
>
>
>>
>> On Thu, Feb 26, 2015 at 11:43 PM, Andrea Giammarchi <
>> andrea.giammarchi@gmail.com> wrote:
>>
>>> AFAIK bluebird did:
>>>
>>> https://github.com/petkaantonov/bluebird/blob/master/API.md#cancelerror-reason---promise
>>>
>>> But I agree once you've made Promises more complex than events ( xhr in
>>> this case ) nobody wins :-/
>>>
>>> Although, specially for fetch or anything network related, there
>>> **must** be a way to bloody cancel that!
>>>
>>> ....right?
>>>
>>>
>>> On Fri, Feb 27, 2015 at 7:31 AM, Kevin Smith <zenparsing@gmail.com>
>>> wrote:
>>>
>>>> The discussion on that github issue surrounding promise subclassing
>>>> makes my head spin, especially when it comes to working out how cancelation
>>>> is supposed to flow through a graph of promise dependencies.  We should be
>>>> wary of adding complexity to the core.
>>>>
>>>> The simple way to view the situation is to say that promises are simply
>>>> transparent containers for asynchronous values. Control capabilities should
>>>> therefore be represented by a separate abstraction. This will help keep
>>>> complexity at the edges.
>>>>
>>>> Has any library experimented with the cancelation token approach yet?
>>>> On Feb 27, 2015 1:46 AM, "Anne van Kesteren" <annevk@annevk.nl> wrote:
>>>>
>>>>> As a heads up, there's some debate around the fetch() API how exactly
>>>>> request termination should work and how that affects promises:
>>>>>
>>>>>   https://github.com/slightlyoff/ServiceWorker/issues/625
>>>>>
>>>>> The WebRTC WG has also been discussing canceling in the context of
>>>>> terminating a request for permission from the user. I think they
>>>>> decided to postpone for now until there's a bit more progress on what
>>>>> cancelable promises means, but I would not expect everyone to wait
>>>>> forever.
>>>>>
>>>>>
>>>>> --
>>>>> https://annevankesteren.nl/
>>>>> _______________________________________________
>>>>> 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
>>>>
>>>>
>>>
>>> _______________________________________________
>>> 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
>
>
Received on Saturday, 28 February 2015 21:15:56 UTC

This archive was generated by hypermail 2.3.1 : Saturday, 28 February 2015 21:16:13 UTC