Skip to content

Instantly share code, notes, and snippets.

@danharper
Last active January 7, 2024 17:58
Show Gist options
  • Save danharper/ad6ca574184589dea28d to your computer and use it in GitHub Desktop.
Save danharper/ad6ca574184589dea28d to your computer and use it in GitHub Desktop.
JavaScript "CancellationToken" for cancelling Async/Promise functions
const CANCEL = Symbol();
class CancellationToken {
constructor() {
this.cancelled = false;
}
throwIfCancelled() {
if (this.isCancelled()) {
throw "Cancelled!";
}
}
isCancelled() {
return this.cancelled === true;
}
[CANCEL]() {
this.cancelled = true;
}
// could probably do with a `register(func)` method too for cancellation callbacks
}
export default class CancellationTokenSource {
constructor() {
this.token = new CancellationToken();
}
cancel() {
this.token[CANCEL]();
}
}

Concept inspired by the CancellationTokenSource and CancellationToken used in C# for cancelling async functions.

Example usage in an async function:
import CancellationTokenSource from './CancellationTokenSource'

async function doStuff(foo, cancellationToken) {
  // do stuff with foo on each significant step call:
  cancellationToken.throwIfCancelled();
  // for example, my use-case is uploading chunks of a file and cancelling the XHR
  return ':)';
}

var cst = new CancellationTokenSource;

doStuff('eee', cst.token)
  .then(result => console.log('result', result))
  .catch(err => console.error('failed', err));
  
setTimeout(() => cst.cancel(), 1000); // auto-cancel after one second
Or.. in a Promise:
import CancellationTokenSource from './CancellationTokenSource'

function doStuff(foo, cancellationToken) {
    return new Promise((resolve, reject) => {
      // do stuff with foo on each significant step call:
      if (cancellationToken.isCancelled()) return reject('Cancelled');
      // for example, my use-case is uploading chunks of a file and cancelling the XHR
      resolve(':)');
  });
}

var cst = new CancellationTokenSource;

doStuff('eee', cst.token)
  .then(result => console.log('result', result))
  .catch(err => console.error('failed', err));
  
setTimeout(() => cst.cancel(), 1000); // auto-cancel after one second
@ritterzk
Copy link

@danharper I think you are looking for the AbortController
https://davidwalsh.name/cancel-fetch

@danharper
Copy link
Author

@ritteratzuken That didn’t exist 6 years ago ;) But yeah, now that it’s in the standard that’s the way to go.

@andrewcourtice
Copy link

@danharper I wrote a version that encapsulates the AbortController inside a Promise so it can be used natively with async/await. We use it internally at the company I work at to terminate async tasks all the way through our state management into the network request and it seems to working well so far :)

Let me know what you think.

https://gist.github.com/andrewcourtice/ef1b8f14935b409cfe94901558ba5594

@ygoe
Copy link

ygoe commented Jan 7, 2024

That cancelled property of the CancellationToken class looks like it could be publicly set to me. So the symbol protection for the cancel() method seems not very effective. I think that should be a private field. Or in good old days before there were classes, a variable in the function closure.

But I'm going to use the AbortController as suggested above. Thanks anyway for providing this connection as I was looking for a CancellationToken for JavaScript. 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment