I'm playing with [Promise Extensions for JavaScript (prex)](https://github.com/rbuckton/prex#readme) library. I want to extend the standard [Promise class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) with cancellation support, similar to how it is [implemented in Bluebird](http://bluebirdjs.com/docs/api/cancellation.html) (i.e., with an optional `oncancel` callback) but using [prex.CancellationToken](https://github.com/rbuckton/prex/blob/master/docs/cancellation.md#class-cancellationtoken).

Here is a draft that can be run with NodeJS, please help improving it:

    const prex = require('prex');
    
    // CancellablePromise
    class CancellablePromise extends Promise {
      constructor(executor, token) {
        console.log('CancellablePromise::constructor');
    
        if (!token) {
          // no token supplied, just delegate to the parent class
          super(executor);
          return;
        }
    
        super((resolve, reject) => {
          let reg = null;
    
          function unregister() {
            // unregister cancellation callback
            if (reg) {
              reg.unregister();
              reg = null;          
            }
          }
      
          function resoveForwarder(v) {
            unregister();
            resolve(v);
          }
    
          function rejectForwarder(e) {
            unregister();
            reject(e);
          }
    
          function oncancelRegistrar(oncancel) {
            if (!oncancel) throw new Error('oncancel');
    
            // register the cancellation callback
            reg = token.register(() => {
              // throw and catch for the extended error info
              try {
                token.throwIfCancellationRequested();
              }
              catch (e) {
                // the token cancellation callback is synchronous
                // (upon `CancellationTokenSource.cancel`),
                // but we aim to invoke oncancel asynchronously
                Promise.resolve().finally(() => {
                  unregister();
                  oncancel(e);
                });
              }
            });
          }
    
          // executor can call `oncancelRegistrar(oncancel)` to 
          // provide its callback to be used for clean up 
          executor(resoveForwarder, rejectForwarder, oncancelRegistrar);
        });
      }
    }
    
    // delayWithCancellation using CancellablePromise
    function delayWithCancellation(timeoutMs, token) {
      console.log('delayWithCancellation');
    
      return new CancellablePromise((resolve, reject, oncancel) => {
        token.throwIfCancellationRequested();
        const id = setTimeout(resolve, timeoutMs);
        oncancel(e => {
          clearTimeout(id);
          reject(e);
        });
      }, token);
    }
    
    // main
    async function main() {
      const tokenSource = new prex.CancellationTokenSource();
      setTimeout(() => tokenSource.cancel(), 1500); // cancel after 1500ms
     
      var token = tokenSource.token;
      await delayWithCancellation(2000, token);
      console.log("successfully delayed."); // we should not be here
    }
    
    main().catch(e => console.log(e));