16 comments

[ 3.1 ms ] story [ 65.6 ms ] thread
Sorely needed, but the proposed implementation feels a little clunky. It's a shame this functionality wasn't included from the get-go so modifying the Promise architecture wasn't such a large consideration.
Implementation shown in slides a bit dull
I'm intrigued. Any way to see an actual presentation?
Not sure if this is needed. A Promise is basically an async representation of a transaction where "cancel" can mean a slew of things. Even w/ `fetch`, what's supposed to happen when you cancel? (connection abort? resource modification reverse?) Cancel should just be a `rejection`, because the Promise's purpose (update a remote resource) wasn't fulfilled
Yes - fetch should abort. https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequ...

Think about an autocomplete search. Every additional letter should abort the prior ajax call and fire a new one. Right now I'm hammering my server because I want to use fetch().

The request is already in flight, it's too late - you can't abort those packets from getting to your server, you can only send new ones (which maybe instruct the server to disregard your search or something).

The correct way to accomplish what you want is to either throttle or debounce your searching such that you reduce the amount of requests made in the first place.

Doesn't matter that you can't cancel in flight packets:

1) because clients throttle connections. Suppose a user wants to navigate, you may need to be able to cancel in flight requests or the next page load might be significantly delayed.

2) file uploads can have potentially gigs of packets that haven't been sent yet, which you definitely need to be able to cancel.

canceling GET is the simplest use case, XMLHttpRequest/fetch is already able to abort. POST/PUT/DELETE, however, modifies remote resources which cannot be easily "canceled" (it might corrupt server state and/or transaction might even have to extend to client being able to fully "resolve" the Promise?)
> POST/PUT/DELETE, however, modifies remote resources which cannot be easily "canceled" (it might corrupt server state and/or transaction might even have to extend to client being able to fully "resolve" the Promise?)

If those can corrupt state then you're already at the complete mercy of the network... which is never a good idea regardless of whether this proposal is accepted or not.

yup, so why bloat up the spec? Introducing a 3rd option that represents a non-deterministic remote state isn't entirely helpful IMO. Having the ability to abort the connection should be enough (which is a rejection).
Hm? My point was that the spec doesn't fundamentally change anything wrt. the things you mentioned, so it should be evaluated on other merits (or lack thereof).

Doing ad-hoc hacks around the lack of cancellation seems like a pretty big practical issue to me[1], so I'd certainly like to see something like this spec in the standard.

(Note: something like, not necessarily exactly this. I haven't thought about it deeply enough to be able to properly evaluate the spec.)

[1] Happens all the time when doing simple Ajax-on-futures where you really just want to ignore any result if the operation gets canceled in the UI before the response arrives.

(comment deleted)
This thread has a much more in-depth discussion on the considerations for designing this API: https://github.com/whatwg/fetch/issues/27

One key point is: since a promise can have multiple then-subscribers interested in it, (a) is it the subscriber's responsibility to signal disinterest (in which case the promise-creator needs to reference-count interest/disinterest)? Or (b) is it the promise-creator's responsibility to unilaterally cancel things, and the entire dependency tree must figure out how to fail elegantly? Either way, in a cancellation world, subscribers need to be rewritten either to (a) indicate disinterest, or (b) handle a cancellation coming from upstream, which is semantically distinct from an error.

Bluebird (whose designers seem to be involved in that thread) handles (a) pretty elegantly - it's subtle, but cancelablePromise.then() increments the reference count and returns a cancelablePromise that, when it or its descendants indicates disinterest, decrements the reference count: http://bluebirdjs.com/docs/api/cancellation.html

But I'm not quite convinced that this needs to be handled at the Promise spec level. If you're going to be adding "finally" clauses anyways, why not just use (b), special-case a type of "catch" and just not toast/console.error the exception if it was a CancellationError?

All this complexity, though, indicates the need for declarative data dependencies a la Relay or Meteor. Making the application programmer handle fetch/cancellation logic will be seen as an anti-pattern once those patterns reach maturity.

It's not that difficult to extend Promises. Show some code that's easy to follow. If cancelled/canceled is too confusing, there are synonyms.

    function longTask() {
      return new AbortablePromise((resolve, reject, onAbort) => {
        const timer = setTimeout(resolve, 1000);
        onAbort(reason => clearTimeout(timer));
      });
    }

    const myTask = longTask
      .aborted(reason => console.log('Aborted'))
      .then(result => console.log('Done'))
      .catch(reason => console.log('Error'));

    setTimeout(() => myTask.abort('Changed my mind'), 300);
I did something similar with reporting progress: https://github.com/numtel/progress-promise
We already have cancelable Promises in custom libs like bluebird, isn't?

So, I want progress reporting for promises too!