DEV Community

loading...

Promise Cancellation & semantic behind it

3Shain
・2 min read

TL;DR: We do not cancel Promise but operations. Cancellation is orthogonal to the abstraction of Promise. They have different concerns.

Many people have asked: I want a Promise with .cancel() method!

Okey. Let's assume we have one.

const promise = fetch('https://somedata.com').then(x=>x.json());

//... at some point
promise.cancel();
Enter fullscreen mode Exit fullscreen mode

To not violate the original Promise/A+ spec, when it's cancelled (and in PENDING status), reject with an error (may name it CancelledError)

And here's the problem: When .cancel() applied, which promise on the chain is really cancelled?

  1. Top(then "downstream" get notified by catching CancelledError and re-throwing it)

    But what if a part of Promises on the chain have resolved? A settled Promise can't modify its state, therefore it can't "notify" its "downstream" again.
    If "upstream" is PENDING, what about other "downstream"s?

    const root = fetch('https://somedata.com');
    const promise1 = root.then(x=>x.json());
    const promise2 = root.then(x=>x.string());
    // ...at some point, root is PENDING
    promise1.cancel(); // what about promise2?
    
  2. Current (but don't care about "upstream")

    Hmmm, it is .ignore() rather than .cancel(). Not really what we expected.

  3. Current (and cancel "upstream", if possible, i.e. no other "downstream" waiting for a value)

    This intuitively makes better sense. But things get really complicated. This is to say explicit dependencies between CancellablePromise need to be managed.....

    Actually the two made-up terms "upstream" and "downstream" do not exist in Promise/A+ spec at all. A Promise has no clue how it will be fulfilled/rejected.

    Maybe RxJS has done this job.

Seems there is no natural behavior and clear semantic of Promise cancellation. Is cancellable Promise just a wrong abstraction?
Think about the question again. Is it the Promise we want to cancel?

IMO we want to cancel should be the operation.

Recall the definition of Promise: A promise represents the eventual result of an asynchronous operation. It promises you there will be a value or error (that's why it is called Promise). While when we talk about a cancellation, we often mean to abort the procedure and if necessary dispose related resources (to prevent memory leak). To strictly speak cancellation is orthogonal to the abstraction of Promise. They have different concerns.

So we need something else to explicitly manage cancellation, e.g. AbortController

// an example of aborting ongoing fetch
const controller = new AbortController();
const signal = controller.signal;

fetch('https://example.com', { signal });

// ...at some point
controller.abort();
Enter fullscreen mode Exit fullscreen mode

I know it's verbose, but to manage cancellation separately makes the most sense.

Discussion (1)

Collapse
3shain profile image
3Shain Author

Actually the first half of this post is leaky, but it might reflect the beginners' intuitions.