Search code examples
promisebluebirdcancellation

how to cancel promises with bluebird


I think I misunderstand how promise cancellation with bluebird works. I wrote a test that demonstrates this. How do I make it green? Thanks:

describe('cancellation tests', () => {
  function fakeFetch() {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(1);
      }, 100);
    });
  }

  function awaitAndAddOne(p1) {
    return p1.then(res => res + 1);
  }

  it('`cancel` cancels promises higher up the chain', () => {
    const p1 = fakeFetch();
    const p2 = awaitAndAddOne(p1);

    expect(p2.isCancellable()).toBeTruthy();
    p2.cancel();

    return p2
      .then(() => {
        console.error('then');
      })
      .catch(err => {
        console.error(err);
      })
      .finally(() => {
        expect(p2.isCancelled()).toBeTruthy(); // Expected value to be truthy, instead received false
        expect(p1.isCancelled()).toBeTruthy(); 
      });
  });
});

Solution

  • @Karen if correct. But the issue is that your test is also a bit wrong

    If you look at the isCancellable method

    Promise.prototype.isCancellable = function() {
        return this.isPending() && !this.isCancelled();
    };
    

    This is just checking if the promise is pending and is not already cancelled. This doesn't mean then cancellation is enabled.

    http://bluebirdjs.com/docs/api/cancellation.html

    If you see the above url, it quotes below

    The cancellation feature is by default turned off, you can enable it using Promise.config.

    And if you look at the cancel method

    Promise.prototype["break"] = Promise.prototype.cancel = function() {
        if (!debug.cancellation()) return this._warn("cancellation is disabled");
    

    Now if I update your test correct like below

    var Promise = require("bluebird");
    var expect = require("expect");
    
    describe('cancellation tests', () => {
        function fakeFetch() {
          return new Promise((resolve, reject) => {
            setTimeout(() => {
              resolve(1);
            }, 100);
          });
        }
    
        function awaitAndAddOne(p1) {
          return p1.then(res => res + 1);
        }
    
        it('`cancel` cancels promises higher up the chain', () => {
          const p1 = fakeFetch();
          const p2 = awaitAndAddOne(p1);
    
          value = p2.isCancellable();
          expect(p2.isCancellable()).toBeTruthy();
          p2.cancel();
    
          expect(p2.isCancelled()).toBeTruthy(); // Expected value to be truthy, instead received false
          expect(p1.isCancelled()).toBeTruthy(); 
        });
      });
    

    You can see that cancellation is not enable and it executes the warning code

    Cancellation is disabled

    The execution results fail as expected

    spec.js:46
      cancellation tests
    spec.js:46
        1) `cancel` cancels promises higher up the chain
    spec.js:78
      0 passing (3m)
    base.js:354
      1 failing
    base.js:370
      1) cancellation tests
    base.js:257
           `cancel` cancels promises higher up the chain:
         Error: expect(received).toBeTruthy()
    Expected value to be truthy, instead received
      false
          at Context.it (test/index.test.js:37:36)
    

    Now if you update the code to enable cancellation

    var Promise = require("bluebird");
    var expect = require("expect");
    
    Promise.config({
      cancellation: true
    });
    
    describe('cancellation tests', () => {
        function fakeFetch() {
          return new Promise((resolve, reject) => {
            setTimeout(() => {
              resolve(1);
            }, 100);
          });
        }
    
        function awaitAndAddOne(p1) {
          return p1.then(res => res + 1);
        }
    
        it('`cancel` cancels promises higher up the chain', () => {
          const p1 = fakeFetch();
          const p2 = awaitAndAddOne(p1);
    
          value = p2.isCancellable();
          expect(p2.isCancellable()).toBeTruthy();
          p2.cancel();
    
          expect(p2.isCancelled()).toBeTruthy(); // Expected value to be truthy, instead received false
          expect(p1.isCancelled()).toBeTruthy(); 
        });
      });
    

    It works!

    Cancellation works