title: Cancel a vanilla ECMAScript 6 Promise chain
body: <p>Is there a method for clearing the <code>.then</code>s of a JavaScript <code>Promise</code> instance?</p>
  <p>I've written a JavaScript test framework on top of <a href="https://qunitjs.com/" rel="noreferrer">QUnit</a>.  The framework runs tests synchronously by running each one in a <code>Promise</code>.  (Sorry for the length of this code block.  I commented it as best I can, so it feels less tedious.)</p>
  <pre><code>/* Promise extension -- used for easily making an async step with a
         timeout without the Promise knowing anything about the function 
         it's waiting on */
  $$.extend(Promise, {
      asyncTimeout: function (timeToLive, errorMessage) {
          var error = new Error(errorMessage || &quot;Operation timed out.&quot;);
          var res, // resolve()
              rej, // reject()
              t,   // timeout instance
              rst, // reset timeout function
              p,   // the promise instance
              at;  // the returned asyncTimeout instance
  
          function createTimeout(reject, tempTtl) {
              return setTimeout(function () {
                  // triggers a timeout event on the asyncTimeout object so that,
                  // if we want, we can do stuff outside of a .catch() block
                  // (may not be needed?)
                  $$(at).trigger(&quot;timeout&quot;);
  
                  reject(error);
              }, tempTtl || timeToLive);
          }
  
          p = new Promise(function (resolve, reject) {
              if (timeToLive != -1) {
                  t = createTimeout(reject);
  
                  // reset function -- allows a one-time timeout different
                  //    from the one original specified
                  rst = function (tempTtl) {
                      clearTimeout(t);
                      t = createTimeout(reject, tempTtl);
                  }
              } else {
                  // timeToLive = -1 -- allow this promise to run indefinitely
                  // used while debugging
                  t = 0;
                  rst = function () { return; };
              }
  
              res = function () {
                  clearTimeout(t);
                  resolve();
              };
  
              rej = reject;
          });
  
          return at = {
              promise: p,
              resolve: res,
              reject: rej,
              reset: rst,
              timeout: t
          };
      }
  });
  
  /* framework module members... */
  
  test: function (name, fn, options) {
      var mod = this; // local reference to framework module since promises
                      // run code under the window object
  
      var defaultOptions = {
          // default max running time is 5 seconds
          timeout: 5000
      }
  
      options = $$.extend({}, defaultOptions, options);
  
      // remove timeout when debugging is enabled
      options.timeout = mod.debugging ? -1 : options.timeout;
  
      // call to QUnit.test()
      test(name, function (assert) {
          // tell QUnit this is an async test so it doesn't run other tests
          // until done() is called
          var done = assert.async();
          return new Promise(function (resolve, reject) {
              console.log(&quot;Beginning: &quot; + name);
  
              var at = Promise.asyncTimeout(options.timeout, &quot;Test timed out.&quot;);
              $$(at).one(&quot;timeout&quot;, function () {
                  // assert.fail() is just an extension I made that literally calls
                  // assert.ok(false, msg);
                  assert.fail(&quot;Test timed out&quot;);
              });
  
              // run test function
              var result = fn.call(mod, assert, at.reset);
  
              // if the test returns a Promise, resolve it before resolving the test promise
              if (result &amp;&amp; result.constructor === Promise) {
                  // catch unhandled errors thrown by the test so future tests will run
                  result.catch(function (error) {
                      var msg = &quot;Unhandled error occurred.&quot;
                      if (error) {
                          msg = error.message + &quot;\n&quot; + error.stack;
                      }
  
                      assert.fail(msg);
                  }).then(function () {
                      // resolve the timeout Promise
                      at.resolve();
                      resolve();
                  });
              } else {
                  // if test does not return a Promise, simply clear the timeout
                  // and resolve our test Promise
                  at.resolve();
                  resolve();
              }
          }).then(function () {
              // tell QUnit that the test is over so that it can clean up and start the next test
              done();
              console.log(&quot;Ending: &quot; + name);
          });
      });
  }
  </code></pre>
  <p>If a test times out, my timeout Promise will <code>assert.fail()</code> on the test so that the test is marked as failed, which is all well and good, but the test continues to run because the test Promise (<code>result</code>) is still waiting to resolve it.</p>
  <p>I need a good way to cancel my test.  I can do it by creating a field on the framework module <code>this.cancelTest</code> or something, and checking every so often (e.g. at the beginning of each <code>then()</code> iteration) within the test whether to cancel out.  However, ideally, I could use <code>$$(at).on(&quot;timeout&quot;, /* something here */)</code> to clear the remaining <code>then()</code>s on my <code>result</code> variable, so that none of the rest of the test is run.</p>
  <p>Does something like this exist?</p>
  <h3>Quick Update</h3>
  <p>I tried using <code>Promise.race([result, at.promise])</code>.  It didn't work.</p>
  <h3>Update 2 + confusion</h3>
  <p>To unblock me, I added a few lines with the <code>mod.cancelTest</code>/polling within the test idea.  (I also removed the event trigger.)</p>
  <pre><code>return new Promise(function (resolve, reject) {
      console.log(&quot;Beginning: &quot; + name);
  
      var at = Promise.asyncTimeout(options.timeout, &quot;Test timed out.&quot;);
      at.promise.catch(function () {
          // end the test if it times out
          mod.cancelTest = true;
          assert.fail(&quot;Test timed out&quot;);
          resolve();
      });
  
      // ...
      
  }).then(function () {
      // tell QUnit that the test is over so that it can clean up and start the next test
      done();
      console.log(&quot;Ending: &quot; + name);
  });
  </code></pre>
  <p>I set a breakpoint in the <code>catch</code> statement, and it's being hit.  What's confusing me now is that the <code>then()</code> statement isn't being called.  Ideas?</p>
  <h3>Update 3</h3>
  <p>Figured the last thing out.  <code>fn.call()</code> was throwing an error which I didn't catch, so the test promise was rejecting before <code>at.promise.catch()</code> could resolve it.</p>