Cancel Promise pattern (no cancellable promises)

Jan-Ivar Bruaroey jib at
Thu Oct 27 02:41:05 UTC 2016

This is an alternative to cancellable promises that relies entirely on 
existing JavaScript.

I'm posting this here in hopes to bring the discussion back to practical 
use cases and minimal needs.


Here's a setTimeout wrapper with cancellation, using a regular promise 
as a cancellation token.

let wait = (ms, cancel) => {
   let id;
   return Promise.race([
     new Promise(resolve => id = setTimeout(resolve, ms)),
     (cancel || new Promise(() => {})).then(() => clearTimeout(id))

function CancelledError() {
   return Object.assign(new Error("The operation was cancelled."), {name: "CancelledError"});

// Demo:

async function foo() {
   try {
     let token = new Promise((r, e) => cancel.onclick = () => e(new CancelledError()));
     await wait(500);
     console.log("Wait 4 seconds...");
     await wait(4000, token);
     console.log("Then wait 3 more seconds...");
     await wait(3000, token);
   } catch (e) {
     if ( != "CancelledError") throw e;
     console.log("User cancelled");

Here's the es6 version to run:

Things to note:
- Cancellation is targeted to specific operations (no "cancel chain" 
- Token can be reused down the chain.
- Cancellation is propagated using a regular (new) CancellationError (no 
third rail).
- It is up to the caller whether to treat cancellations as non-exceptional.
- Basic Promise.race pattern works even to wrap APIs that aren't 
cancellable (stop waiting)
- Pattern allows substituting any error (though I hope we standardize 
- Pattern allows chain resumption by resolving token with any desired 
value instead.

I don't think this group needs to develop much here, maybe standardize 
CancelledError, and have fetch() take a cancel promise argument like 
wait() does in the example above.

I'm open to hearing what use-cases are not be covered by this.

Looking forward to your feedback. Thanks!

.: Jan-Ivar :.

More information about the es-discuss mailing list