Search code examples
promisetimeoutbluebird

How fast / efficient is Bluebird's timeout?


The following example times out in most cases (outputs timed out):

Promise = require('bluebird');

new Promise(resolve => {
    setTimeout(resolve, 1000);
})
    .timeout(1001)
    .then(() => {
        console.log('finished');
    })
    .catch(error => {
        if (error instanceof Promise.TimeoutError) {
            console.log('timed out');
        } else {
            console.log('other error');
        }
    });

Does this mean that the Bluebird's promise overhead takes longer than 1ms? I see it often time out even if I use .timeout(1002).

The main reason for asking - I'm trying to figure what the safe threshold is, which gets more important with smaller timeouts.


Using Bluebird 3.5.0, under Node.js 8.1.2


Solution

  • I have traced your bug in Bluebird's code. Consider this:

    const p = new Promise(resolve => setTimeout(resolve, 1000));
    const q = p.timeout(1001); // Bluebird spawns setTimeout(fn, 1001) deep inside
    

    That looks rather innocent, yeah? Though, not in this case. Internally, Bluebird implemented it something like (not actually valid JS; timeout clearing logic is omitted):

    Promise.prototype.timeout = function(ms) {
        const original = this; 
        let result = original.then(); // Looks like noop
        setTimeout(() => {
             if result.isPending() { 
                make result rejected with TimeoutError; // Pseudocode
             }
        }, ms);
        return result;
    }
    

    Bug was presence of line ret.isPending(). It resulted in brief time when original.isPending() === false and ret.isPending() === true because "resolved" status didn't propagate yet from original to children. Your code hit that extremely short period and BOOM, you had race condition.