Bluebird超时的速度/效率有多快?

时间:2017-06-28 23:43:45

标签: promise timeout bluebird

以下示例在大多数情况下超时(输出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');
        }
    });

这是否意味着Bluebird的承诺开销需要超过1毫秒? 即使我使用.timeout(1002),我也会看到它经常超时。

询问的主要原因 - 我正在试图确定安全阈值是什么,哪个更重要,更短的超时。

在Node.js 8.1.2下使用Bluebird 3.5.0

2 个答案:

答案 0 :(得分:1)

我已经在Bluebird的代码中追踪了你的错误。考虑一下:

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

看起来很无辜,是吗?虽然,不是在这种情况下。在内部,Bluebird实现了类似的东西(实际上并不是有效的JS;省略了超时清除逻辑):

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存在行ret.isPending()。它导致original.isPending() === falseret.isPending() === true的短暂时间,因为"已解决"状态尚未从original传播到儿童。你的代码达到极短的时间和BOOM,你有竞争条件。

答案 1 :(得分:0)

我认为这里发生的事情是,承诺链的其余部分与.timeout()的计时器之间存在竞争。因为他们在时间上都非常接近,所以有时会获胜,有时会获胜 - 他们很有活力。当我运行记录事件序列的代码时,我会在不同的运行中得到不同的顺序。确切的输出顺序是不可预测的(例如racy)。

const Promise = require('bluebird');

let buffer = [];
function log(x) {
    buffer.push(x);
}

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


setTimeout(() => {
    console.log(buffer.join("\n"));
}, 2000);    

有时输出:

hit my timeout
finished

而且,有时输出:

hit my timeout
timed out

正如评论中所提到的,如果.then()总是通过微任务执行(应该在任何宏任务之前),那么人们会认为.then()会先于setTimeout() .timeout(),但事情显然不是那么简单。

由于promise .then()调度的细节不是规范要求的(只有堆栈没有应用程序代码),代码设计不应该采用特定的调度算法。因此,接近执行它所遵循的异步操作的超时可能是活泼的,因此是不可预测的。

如果你能准确解释你想要解决的问题,我们可能会提供更多具体建议。 Javascript中没有定时器对于ms是精确的,因为JS是单线程的,并且所有定时器事件都必须通过事件队列,并且它们仅在事件被服务时调用它们的回调(不是在定时器触发时)。也就是说,定时器事件将始终按顺序提供,因此setTimeout(..., 1000)将始终位于setTimeout(..., 1001)之前,即使执行两个回调之间可能没有正好1ms的增量。