I want to add a cancel
method to a subclass of the Promise
built-in. Why does this not work?
class CancellablePromise extends Promise {
constructor(executor) {
let cancel = null
super((resolve,reject) => {
cancel = reject
executor(resolve, reject)
})
this.cancel = cancel
}
}
const p = new CancellablePromise((resolve) => setTimeout(resolve, 1000))
.then(() => console.log('success'))
.catch((err) => console.log('rejected', err))
p.cancel() // Uncaught exception
Is the answer todo with Symbol.species
?
The problem is that then
, catch
, and finally
create and return a new promise, and the new promise that they create and return doesn't have the cancel
method on it.
To fix that, you'd have to override then
so it copies cancel
from the current instance to the new one:
class CancellablePromise extends Promise {
constructor(executor) {
let cancel = null;
super((resolve,reject) => {
cancel = reject;
executor(resolve, reject);
});
this.cancel = cancel;
}
then(onFulfilled, onRejected) {
const p = super.then(onFulfilled, onRejected);
p.cancel = this.cancel;
return p;
}
}
const p = new CancellablePromise((resolve) => setTimeout(resolve, 1000))
.then(() => console.log('success'))
.catch((err) => console.log('rejected', err));
p.cancel();
You don't need to do catch
or finally
, they're both defined using calls to then
(per specification).
I should note that there are a lot of nuances around cancellable promises that I haven't gotten into in detail. It may be worth a deep read of Domenic Denicola's old (now withdrawn) cancellable promises proposal and this article by Ben Lesh.