Updated, I'm playing with Promise Extensions for JavaScript (prex) library and I now uselike it a much simplified version oflot. Amongst other things, this library appears to be a prototype behind the current CancellablePromise
ECMA TC39 proposal for cancellation and it uses the familiar cancellation token approach, I'll planpopularized by .NET Task API.
I want to publishextend the standard Promise class with cancellation support, similar to how it asis implemented in Bluebird (i.e., with an independent NPM package soonoptional oncancel
callback) but using prex.CancellationToken.
Here is a draft that can be run with NodeJS:
/**const prex = require('prex');
class *CancellablePromise Classextends representingPromise a{
cancellable promise.constructor(executor, token) {
* @extends Promise if (!token) {
* /
class/ CancellablePromiseif extendsno Promisetoken {supplied, just delegate to the parent class
static get [Symbol.species] super(executor) {;
return Promise; return;
}
/**
* Createconst anobserveCancellation instance= ofasync CancellablePromise() promise.=> {
* @param {Function} executor// -prex.Deferred acceptsis ansimilar objectto withTaskCompletionSource callbacksin .NET
* and aconst token:deferred {= resolve,new reject,prex.Deferred();
cancel, token }
*executor(
@param {CancellationToken} token - a cancellation token deferred.resolve,
*/
constructor(executor deferred.reject, token)
{ cancelListener =>
const withCancellation = async ( deferred.cancelListener = cancelListener); => {
const linkedSourceregistration = new CancellationTokenSource(token.register(();
=> {
try {
const linkedToken =// linkedSource.token;capture the CancelError
linkedToken token.throwIfCancellationRequested();
const}
deferred = new Deferred catch (cancelError); {
linkedToken.register(() => deferred.cancel());try {
// the token cancellation callback is synchronous,
// and so is the executor({-provided cancelListener callback
resolve: value =>deferred.cancelListener && deferred.resolvecancelListener(valuecancelError),;
// reject: errorhere =>if cancelListener has not resolved/rejected it
deferred.reject(errorcancelError),;
cancel:}
catch (error) =>{
linkedSource // in case cancelListener throws
deferred.cancelreject(error),;
token: linkedToken,}
}
});
try {
return await deferred;deferred.promise;
}
finally {
//registration.unregister();
this will free the linkedToken registration}
};
linkedSource.closesuper((resolve, reject); =>
observeCancellation().then(resolve, reject));
}
}
// delayWithCancellation
function delayWithCancellation(timeoutMs, token) {
console.log(`delayWithCancellation: ${timeoutMs}`);
return new superCancellablePromise((resolve, reject, setCancelListener) => withCancellation{
token.throwIfCancellationRequested().then;
const id = setTimeout(resolve, rejecttimeoutMs);
setCancelListener(e => clearTimeout(id));
}, token);
}
// main
async function main() {
const tokenSource = new prex.CancellationTokenSource();
setTimeout(() => tokenSource.cancel(), 2000); // cancel after 1500ms
const token = tokenSource.token;
await delayWithCancellation(1000, token);
console.log("successfully delayed."); // we should reach here
await delayWithCancellation(1500, token);
console.log("successfully delayed."); // we should not reach here
}
main().catch(error => console.log(error));