Monthly Archives: September 2015

Cancellable promise

I write a lot of promise functions everyday. Promise is very useful in many ways, especially  to handle nested callbacks and avoid callback hells. If you have used Promise long enough, you will probably know that Promise is not lazy and also not cancellable. Once you make a promise, you have to stick with it. But for my experience, Promise cancel is necessary sometime, especially when  the promise may take a long time and you want to let user cancel it, or you want to ignore all the chaining callbacks of the promise..

Promise cancellation does not make into ES6 standard yet, and the specification is being discussed here. When a promise is cancelled, it will propagate to the farthest pending promises and reject these promises with a the cancel reason or CancellationError. There are a few promise library that supports cancellation already like BlueBird or Google Closure. But until the spec is approved and you don’t want to bring in these library, can we cannel a promise? The answer is yes, we can do this with Promise.defer and Promise.race.

First of all, let see Promise.defer: Promise.defer return a deferred object. The deferred object is like a promise controller, it allows you to resolve or reject the promise. A promise can’t change the state by itself, only the executor can resolve or reject a promise. You can think deferred like inverse the control outside of the promise itself. Deferred is not a Promises/A+ standard, but you can easily implement with pure javascript. I am going to borrow the code from BlueBird.

What’s about Promise.race? Promise.race takes an array of promises and return a promise that resolve or reject with the value of the earliest promise in the array. This is ES6 standard and a very cool method, you can do a lot of interesting things with it. For example, below is an implementation of time in promise using Promise.race

Now you know deferred and Promise.race, the idea of cancellable promise is actually very simple: it’s a race between the original promise and a deferred. If you cancel, you reject the deferred promise and therefore reject the chain. If you just let it finishes, it will use the resolved result or rejected error from the original promise. Below is a prototype implementation.

Now you can use cancellablePromise to create a promise then cancel it whenever you want.

You can edit the function to throw out a custom Error class and catch it to handle the cancellation correctly. Just a kindly reminder that the above code may not be bug-freed, so be very careful but if you find any problem just let me know 😀