Skip to main content
Links
Source Link

Updated, below is a more current and simple version of this. Also, if you track the progress of TC39's "ECMAScript Cancellation" proposal, this thread might be worth checking out.


I'm playing with Promise Extensions for JavaScript (prex) library and I like it a lot. Amongst other things, this library appears to be a prototype behind the current ECMA TC39 proposal for cancellation and it uses the familiar cancellation token approach, popularized by .NET Task API.

I'm playing with Promise Extensions for JavaScript (prex) library and I like it a lot. Amongst other things, this library appears to be a prototype behind the current ECMA TC39 proposal for cancellation and it uses the familiar cancellation token approach, popularized by .NET Task API.

Updated, below is a more current and simple version of this. Also, if you track the progress of TC39's "ECMAScript Cancellation" proposal, this thread might be worth checking out.


I'm playing with Promise Extensions for JavaScript (prex) library and I like it a lot. Amongst other things, this library appears to be a prototype behind the current ECMA TC39 proposal for cancellation and it uses the familiar cancellation token approach, popularized by .NET Task API.

Rollback to Revision 4
Source Link
Mast
  • 13.5k
  • 12
  • 54
  • 119

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 CancellablePromiseECMA 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));

Updated, I now use a much simplified version of CancellablePromise, I'll plan to publish it as an independent NPM package soon:

/**
 * Class representing a cancellable promise.
 * @extends Promise
 */
class CancellablePromise extends Promise {
  static get [Symbol.species]() {
    return Promise;
  }
 
  /**
   * Create an instance of CancellablePromise promise.
   * @param {Function} executor - accepts an object with callbacks 
   *  and a token: { resolve, reject, cancel, token }
   * @param {CancellationToken} token - a cancellation token.
   */
  constructor(executor, token) {
    const withCancellation = async () => {
      const linkedSource = new CancellationTokenSource(token);

      try {
        const linkedToken = linkedSource.token;
        linkedToken.throwIfCancellationRequested();
 
        const deferred = new Deferred();
        linkedToken.register(() => deferred.cancel());

        executor({ 
          resolve: value => deferred.resolve(value),
          reject: error => deferred.reject(error),
          cancel: () => linkedSource.cancel(),
          token: linkedToken,
        });
 
        return await deferred;
      }
      finally {
        // this will free the linkedToken registration
        linkedSource.close();
      }
    };

    super((resolve, reject) => withCancellation().then(resolve, reject));
  }
}

I'm playing with Promise Extensions for JavaScript (prex) library and I like it a lot. Amongst other things, this library appears to be a prototype behind the current ECMA TC39 proposal for cancellation and it uses the familiar cancellation token approach, popularized by .NET Task API.

I want to extend the standard Promise class with cancellation support, similar to how it is implemented in Bluebird (i.e., with an optional oncancel callback) but using prex.CancellationToken.

Here is a draft that can be run with NodeJS:

const prex = require('prex');

class CancellablePromise extends Promise {
  constructor(executor, token) {
    if (!token) {
      // if no token supplied, just delegate to the parent class
      super(executor);
      return;
    }
    
    const observeCancellation = async () => {
      // prex.Deferred is similar to TaskCompletionSource in .NET
      const deferred = new prex.Deferred();

      executor(
        deferred.resolve, 
        deferred.reject,  
        cancelListener => 
          deferred.cancelListener = cancelListener);   

      const registration = token.register(() => {
        try {
          // capture the CancelError
          token.throwIfCancellationRequested();
        }
        catch (cancelError) {
          try {
            // the token cancellation callback is synchronous,
            // and so is the executor-provided cancelListener callback
            deferred.cancelListener && deferred.cancelListener(cancelError);
            // reject here if cancelListener has not resolved/rejected it
            deferred.reject(cancelError);
          }
          catch (error) {
            // in case cancelListener throws
            deferred.reject(error);
          }
        }
      });
    
      try {
        return await deferred.promise;
      }
      finally {
        registration.unregister();
      }
    };

    super((resolve, reject) => 
      observeCancellation().then(resolve, reject));
  }
}

// delayWithCancellation
function delayWithCancellation(timeoutMs, token) {
  console.log(`delayWithCancellation: ${timeoutMs}`);

  return new CancellablePromise((resolve, reject, setCancelListener) => {
    token.throwIfCancellationRequested();
    const id = setTimeout(resolve, timeoutMs);
    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));
New code
Source Link

I'm playing with Promise Extensions for JavaScript (prex) library and I like it a lot. Amongst other things, this library appears to be a prototype behind the current ECMA TC39 proposal for cancellation and it uses the familiar cancellation token approachUpdated, popularized by .NET Task API.

I want to extend the standardnow use a much simplified version of Promise class with cancellation supportCancellablePromise, similarI'll plan to howpublish it is implemented in Bluebird (i.e., withas an optional oncancel callback) but using prex.CancellationToken.

Here is a draft that can be run with NodeJSindependent NPM package soon:

const prex = require('prex');
/**
class CancellablePromise extends Promise* {
Class representing constructor(executor,a token)cancellable {promise.
    if* (!token)@extends {Promise
      /*/ if no token supplied, just delegate
class toCancellablePromise theextends parentPromise class{
    static get super[Symbol.species](executor);
 {
    return return;Promise;
    }
    
    const observeCancellation = async () => {/**
      // prex.Deferred* isCreate similaran toinstance TaskCompletionSourceof inCancellablePromise promise.NET
      const* deferred@param ={Function} newexecutor prex.Deferred();

- accepts an object with callbacks executor(
   *  and a token: { deferred.resolve, 
   reject, cancel, token }
  deferred.reject, 
 * @param {CancellationToken} token - a cancellation token.
 cancelListener => */
   constructor(executor, token) {
    const deferred.cancelListenerwithCancellation = cancelListenerasync (); => 
 {
      const registrationlinkedSource = token.register(new CancellationTokenSource(token) => {;
   
      try {
          //const capturelinkedToken the= CancelErrorlinkedSource.token;
          tokenlinkedToken.throwIfCancellationRequested();
      
   }
     const deferred = catchnew Deferred(cancelError) {;
         linkedToken.register(() try=> {deferred.cancel());
            // the token cancellation callback is synchronous,
            // and so is the executor-provided cancelListener({ callback
           resolve: deferred.cancelListenervalue &&=> deferred.cancelListenerresolve(cancelErrorvalue);,
            // reject here if cancelListener has not resolved/rejected it
         : error => deferred.reject(cancelErrorerror);
          },
          catchcancel: (error) {
            // in case cancelListener throws
           => deferredlinkedSource.rejectcancel(error);,
          }
     token: linkedToken,
  }
      });
    
      try {
        return await deferred.promise;deferred;
      }
      finally {
        registration.unregister();
    // this }
will free the linkedToken };
registration
    super((resolve, reject) => 
       observeCancellation()linkedSource.thenclose(resolve, reject));
  }
}

// delayWithCancellation
function delayWithCancellation(timeoutMs, token) {}
  console.log(`delayWithCancellation: ${timeoutMs }`);

  return new CancellablePromisesuper((resolve, reject, setCancelListener) => {
    token.throwIfCancellationRequestedwithCancellation();
    const id = setTimeout.then(resolve, timeoutMs);
    setCancelListener(e => clearTimeout(idreject));
  }, 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));

I'm playing with Promise Extensions for JavaScript (prex) library and I like it a lot. Amongst other things, this library appears to be a prototype behind the current ECMA TC39 proposal for cancellation and it uses the familiar cancellation token approach, popularized by .NET Task API.

I want to extend the standard Promise class with cancellation support, similar to how it is implemented in Bluebird (i.e., with an optional oncancel callback) but using prex.CancellationToken.

Here is a draft that can be run with NodeJS:

const prex = require('prex');

class CancellablePromise extends Promise {
  constructor(executor, token) {
    if (!token) {
      // if no token supplied, just delegate to the parent class
      super(executor);
      return;
    }
    
    const observeCancellation = async () => {
      // prex.Deferred is similar to TaskCompletionSource in .NET
      const deferred = new prex.Deferred();

      executor(
        deferred.resolve, 
        deferred.reject, 
         cancelListener => 
          deferred.cancelListener = cancelListener);  
 
      const registration = token.register(() => {
        try {
          // capture the CancelError
          token.throwIfCancellationRequested();
        }
        catch (cancelError) {
          try {
            // the token cancellation callback is synchronous,
            // and so is the executor-provided cancelListener callback
            deferred.cancelListener && deferred.cancelListener(cancelError);
            // reject here if cancelListener has not resolved/rejected it
            deferred.reject(cancelError);
          }
          catch (error) {
            // in case cancelListener throws
            deferred.reject(error);
          }
        }
      });
    
      try {
        return await deferred.promise;
      }
      finally {
        registration.unregister();
      }
    };

    super((resolve, reject) => 
       observeCancellation().then(resolve, reject));
  }
}

// delayWithCancellation
function delayWithCancellation(timeoutMs, token) {
  console.log(`delayWithCancellation: ${timeoutMs}`);

  return new CancellablePromise((resolve, reject, setCancelListener) => {
    token.throwIfCancellationRequested();
    const id = setTimeout(resolve, timeoutMs);
    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));

Updated, I now use a much simplified version of CancellablePromise, I'll plan to publish it as an independent NPM package soon:

/**
 * Class representing a cancellable promise.
 * @extends Promise
 */
class CancellablePromise extends Promise {
  static get [Symbol.species]() {
    return Promise;
  }

  /**
   * Create an instance of CancellablePromise promise.
   * @param {Function} executor - accepts an object with callbacks 
   *  and a token: { resolve, reject, cancel, token }
   * @param {CancellationToken} token - a cancellation token.
   */
  constructor(executor, token) {
    const withCancellation = async () => {
      const linkedSource = new CancellationTokenSource(token);
 
      try {
        const linkedToken = linkedSource.token;
        linkedToken.throwIfCancellationRequested();
 
        const deferred = new Deferred();
        linkedToken.register(() => deferred.cancel());

        executor({ 
          resolve: value => deferred.resolve(value),
          reject: error => deferred.reject(error),
          cancel: () => linkedSource.cancel(),
          token: linkedToken,
        });
 
        return await deferred;
      }
      finally {
        // this will free the linkedToken registration
        linkedSource.close();
      }
    };

    super((resolve, reject) => withCancellation().then(resolve, reject));
  }
}
Tweeted twitter.com/StackCodeReview/status/1178323697763000321
Wording
Source Link
Loading
Comments
Source Link
Loading
Code
Source Link
Loading
Post Undeleted by noseratio
Post Deleted by noseratio
Source Link
Loading