JavaScript Concurrency: Canceling Promises
Welcome to the Canceling Promises lesson!
This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!
Some concurrency systems have built-in support for canceling asynchronous code, but JavaScript promises don't have this feature. Once we've attached a
thencallback to a promise, there's no built-in way to stop it. If we need to cancel an operation, it's up to us to implement the cancelation ourselves.One approach is to have a "canceled" flag that we check. If the flag is set to
trueby the time the code inside thethenruns, we can cancel the work.The example below implements cancelation using this kind of
canceledflag. We set the flag totrueafter 500 ms, but the work doesn't start until 1000 ms. Ourthencallback checks the flag, sees thatcanceledistrue, and returns early. We don't provide a return value, so it will returnundefined.>
let canceled = false;const promise = new Promise(resolve => setTimeout(resolve, 1000)).then(() => {if (canceled === true) {return;}return 'it ran';});// Set the canceled flag after 500 mssetTimeout(() => { canceled = true; }, 500);promise;Async Result:
{fulfilled: undefined}What if we have a lot of slow steps in our promise chain? In that case, we should probably support cancelation at each step. Directly checking for cancelation in every
thencallback would be cumbersome, but fortunately there's a better way.We can extract the cancelation check into a
throwIfCanceledfunction. That function throws an exception if thecanceledflag is true. As usual, any exception thrown inside athencallback leads that promise to reject. The net result is: any cancelation will reject the entire promise chain.>
let canceled = false;function throwIfCanceled() {if (canceled) {throw new Error('Canceled');}}const promise = new Promise(resolve => setTimeout(resolve, 1000)).then(() => 5).then(n => {throwIfCanceled();return n * 2;}).then(n => {throwIfCanceled();return n + 1;});// Set the canceled flag after 500 mssetTimeout(() => { canceled = true; }, 500);promise;Async Result:
{rejected: 'Error: Canceled'}The examples in this lesson used a
canceledvariable declared in the code immediately outside of the promise. However, the flag can live anywhere. For example, slow promise chains in production systems in web backends are more likely to use a cancelation flag value stored in a database, checking the database flag at a few critical points in the process.One final note on cancelation. An earlier lesson mentioned Bluebird, which is an implementation of promises in pure JavaScript. Bluebird promises have built-in support for cancelation, as do some other promise libraries written in JavaScript.
If you find yourself working on a project that uses one of those libraries, you may have access to built-in cancelation. However, most new JavaScript code is written using native promises, so we stick to native promises in this course. We recommend that you do the same and use native promises when possible, which means doing cancelation manually.