Last active
September 7, 2020 11:15
Cancellable JavaScript Promise
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// by @noseratio | |
// https://gist.github.com/noseratio/141a2df292b108ec4c147db4530379d2 | |
// https://runkit.com/noseratio/cancellablepromise | |
const prex = require('prex'); | |
/** | |
* A cancellable promise. | |
* @extends Promise | |
*/ | |
class CancellablePromise extends Promise { | |
static get [Symbol.species]() { | |
// https://tinyurl.com/promise-constructor | |
return Promise; | |
} | |
constructor(executor, token) { | |
const withCancellation = async () => { | |
// create a new linked token source | |
const linkedSource = new prex.CancellationTokenSource(token? [token]: []); | |
try { | |
const linkedToken = linkedSource.token; | |
const deferred = new prex.Deferred(); | |
linkedToken.register(() => deferred.reject(new prex.CancelError())); | |
executor({ | |
resolve: value => deferred.resolve(value), | |
reject: error => deferred.reject(error), | |
token: linkedToken | |
}); | |
await deferred.promise; | |
} | |
finally { | |
// this will also free all linkedToken registrations, | |
// so the executor doesn't have to worry about it | |
linkedSource.close(); | |
} | |
}; | |
super((resolve, reject) => withCancellation().then(resolve, reject)); | |
} | |
} | |
/** | |
* A cancellable delay. | |
* @extends Promise | |
*/ | |
class Delay extends CancellablePromise { | |
static get [Symbol.species]() { return Promise; } | |
constructor(delayMs, token) { | |
super(r => { | |
const id = setTimeout(r.resolve, delayMs); | |
r.token.register(() => clearTimeout(id)); | |
}, token); | |
} | |
} | |
// main | |
async function main() { | |
const tokenSource = new prex.CancellationTokenSource(); | |
const token = tokenSource.token; | |
setTimeout(() => tokenSource.cancel(), 2000); // cancel after 2000ms | |
let delay = 1000; | |
console.log(`delaying by ${delay}ms`); | |
await new Delay(delay, token); | |
console.log("successfully delayed."); // we should reach here | |
delay = 2000; | |
console.log(`delaying by ${delay}ms`); | |
await new Delay(delay, token); | |
console.log("successfully delayed."); // we should not reach here | |
} | |
main().catch(error => console.error(`Error caught, ${error}`)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment