Cancellable Promise
Write cancellable(promise) returning { promise, cancel }.
The returned promise settles exactly as the original does, except that calling cancel() before it settles rejects it with the string "Cancelled". Calling cancel() afterwards must do nothing.
Examples
const { promise, cancel } = cancellable(
new Promise((r) => setTimeout(() => r('done'), 500))
);
cancel();
promise.catch(console.log);"Cancelled"Constraints
- cancel may be called any number of times
Notes
- This stops you waiting; it does not stop the work. `AbortController` is what actually aborts a fetch.
Hints
Cancellable Promise (reference solution)
One way to do it. The Editorial tab walks through why it is written this way.
Solution
1function cancellable(promise) {
2 let cancel;
3 const gate = new Promise((_, reject) => { cancel = () => reject('Cancelled'); });
4 gate.catch(() => {});
5 return { promise: Promise.race([promise, gate]), cancel };
6}Editorial: Cancellable Promise
A promise you can walk away from
A promise has no cancel. Once created it will settle, and the only question is whether anyone is still listening. Cancellation is therefore about the listener, not the work.
Approach
Make a second promise whose reject you keep, and race it against the original.
Implementation
function cancellable(promise) { let cancel; const gate = new Promise((_, reject) => { cancel = () => reject('Cancelled'); }); gate.catch(() => {}); return { promise: Promise.race([promise, gate]), cancel }; }
Worth knowing
You need no "has it settled" flag: a promise settles once, so a cancel() after the fact is a no-op for free.
Be honest about what this does. The underlying work carries on — the timer still fires, the request still completes and its response is still downloaded. AbortController passed to fetch genuinely aborts the request, and is what you would use in a component that unmounts mid-flight.