“返回Promise.resolve()”如何影响JavaScript事件循环?

时间:2018-05-08 15:28:59

标签: javascript ecmascript-6 es6-promise event-loop

我一直在使用Javascript,我理解javascript-event-loop是如何工作的。但是,我遇到了一个对我来说没有意义的案例。 请考虑以下代码:

setTimeout(()=> console.log('1'));
Promise.resolve('whatever').then(()=>console.log('2'));
console.log('3');

我期待这个输出:

  

3
  1
  2

但是,我在chrome js控制台上运行它,我得到以下内容:

  

3
  2
  1

问题:不应该“Promise.resolve()。then(fn)”要么立即调用函数,要么将函数执行过程插入到事件循环的末尾 - 'setTimeout'的工作方式 - ,有什么我想念的吗?

2 个答案:

答案 0 :(得分:3)

浏览器实现多个作业/任务队列。 The spec requires implementations to maintain two queues

  1. ScriptQueue
  2. PromiseQueue
  3. 很可能还有一个TimerQueue(HTML DOM任务队列),它也适用于HTML DOM Level 2 Timer规范。这些是在运行时填充的每个FIFO队列,最终是the event loop queue。按照您的代码示例:

    setTimeout(()=> console.log('1'));
    Promise.resolve('whatever').then(()=>console.log('2'));
    console.log('3');
    
    • 第1行推送(可能)TimerQueue(HTML DOM任务队列)
    • 第2行推进 PromiseQueue
    • 第3行被推入堆栈并立即执行 (完成)

    一旦堆栈为空,每个队列将排空直到空。在您的示例中,Promise队列首先清空,然后TimerQueue清空。

    通过稍微扩展您的示例可以进一步证明这一点:

    setTimeout(()=> console.log('second from final')); // <-- first item in TimerQueue
    Promise.resolve('whatever').then(()=>console.log('2')); //<-- first time in PromiseQueue
    setTimeout(()=> console.log('just prior to final')); // <-- second item in TimerQueue
    Promise.resolve('whatever').then(()=>console.log('1')); //<-- second item in PromiseQueue
    setTimeout(()=> console.log('final')); // <-- third item in ScriptQueue 
    console.log('3'); // <-- synchrounous function call placed onto the stack, executed immediately

    您应该注意执行顺序保证。规范定义队列的执行顺序:

      

    此规范未定义多个Job的顺序   队列服务。 ECMAScript实现可以交织   用于评估作业队列的PendingJob记录的FIFO评估   评估一个或多个其他作业队列的PendingJob记录。

    修改

    在与Bergi讨论(下面)之后,我已经表明浏览器实现可能也会创建其他队列。与此帖相关的最可能的队列是TimerQueue作为任务队列,其中包含HTML DOM规范的计时器任务。

答案 1 :(得分:0)

setTimeout表现为细微的规则集。例如,在多个浏览器上,最小超时为4ms, not 0ms

你是正确的,Promise不会立即执行,但会等待一个干净的堆栈运行。

还有一些技巧,比如添加一个标签来强行将某些东西撞到事件循环的顶部。请参阅setImmediate针对浏览器的polyfill