"return Promise.resolve()" 如何影响 JavaScript 事件循环?
How does "return Promise.resolve()" affect the JavaScript 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' 的工作方式 - 是吗我缺少什么?
setTimeout 遵循一组微妙的规则。例如,在一些浏览器上,最小超时为 4ms, not 0ms.
你是正确的,Promise 不会立即执行,而是等待一个干净的堆栈到 运行。
还有一些技巧,例如添加标签以强制将某些内容撞到事件循环的顶部。查看 setImmediate 浏览器的 polyfill
浏览器实现多个 job/task 队列。 The spec requires implementations to maintain two queues:
- 脚本队列
- 承诺队列
很可能还有一个 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 行推送到
承诺队列
- 第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
您应该注意,不能保证执行顺序。规范没有定义队列的执行顺序:
This specification does not define the order in which multiple Job
Queues are serviced. An ECMAScript implementation may interweave the
FIFO evaluation of the PendingJob records of a Job Queue with the
evaluation of the PendingJob records of one or more other Job Queues.
编辑
在与 Bergi 讨论(如下)之后,我指出浏览器实现也可能创建其他队列。与此 post 相关的最有可能的队列是一个 TimerQueue 作为任务队列,其中包含 HTML DOM 规范的计时器任务。
我使用 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' 的工作方式 - 是吗我缺少什么?
setTimeout 遵循一组微妙的规则。例如,在一些浏览器上,最小超时为 4ms, not 0ms.
你是正确的,Promise 不会立即执行,而是等待一个干净的堆栈到 运行。
还有一些技巧,例如添加标签以强制将某些内容撞到事件循环的顶部。查看 setImmediate 浏览器的 polyfill
浏览器实现多个 job/task 队列。 The spec requires implementations to maintain two queues:
- 脚本队列
- 承诺队列
很可能还有一个 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 行推送到 承诺队列
- 第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
您应该注意,不能保证执行顺序。规范没有定义队列的执行顺序:
This specification does not define the order in which multiple Job Queues are serviced. An ECMAScript implementation may interweave the FIFO evaluation of the PendingJob records of a Job Queue with the evaluation of the PendingJob records of one or more other Job Queues.
编辑
在与 Bergi 讨论(如下)之后,我指出浏览器实现也可能创建其他队列。与此 post 相关的最有可能的队列是一个 TimerQueue 作为任务队列,其中包含 HTML DOM 规范的计时器任务。