setTimeout(fn 0)与Promise的执行顺序

Author Avatar
AppleSun 7月 13, 2018

一道爆款面试题,考察 开发者对于promise 和 setTimeout的执行时间的情况,下面开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
setTimeout(function() {
console.log(1)
}, 0)
new Promise(function(resolve, reject) {
console.log(2)
for(let i = 0; i < 1000; i++) {
if(i === 10) {console.log(10)}
i === 999 && resolve()
}
console.log(3)
}).then(function() {
console.log(4)
})

for (let i = 0; i < 3; i++) {
console.log('a' + i)
}
console.log(5)

我将面试题常见的关于 Promise setTimeout 的执行时间的题目做了一下汇集,正确理解 这段代码的执行顺序 可以很好的帮我们处理 异步 调用时的 执行顺序

带着你的答案看下面

The answer is :

2
10
3
a0
a1
a2
5
4
undefined  // 函数的返回值
1

拆开来分析

setTimeout(fn, 0)何时执行?

我们知道,JavaScript是基于事件驱动单线程执行的,所有任务都需要排队,也就是说前一个任务结束,才会去执行下一个任务。而像settimeout、ajax等异步操作的回调,会进入”任务队列“中,而且只有主线程中没有执行任何同步代码的前提下,才会执行异步回调。 而settimeout(fn, 0)表示立即执行,也就是用来改变任务的执行顺序,要求浏览器”尽可能快“的进行回调。

Promise何时执行

1
2
3
4
5
6
7
8
new Promise(function(resolve, reject) {
console.log(2)
for(let i = 0; i < 1000; i++) {
if(i === 10) {console.log(10)}
i === 999 && resolve()
}
console.log(3)
})

结果: 2 10 3 从结果可以看出

Promise新建后立即执行,也就是说,Promise构造函数里的代码是同步执行的

问题:为何 then作为异步函数 会早于 setTimeout触发呢?

(1)setTimeout中的 0 并不是0s执行

setTimeout有个最小执行时间(minimum delay of 4ms ),并不是0s执行的。

注:HTML5中已经将最小执行时间统一为4ms。

(2)macrotask 与 microtask

  • macrotask

常用如:setTimeout setInterval setImmediate I/O UI rendering

  • microtask

常用如:process.nextTick Promise MutationObserver

一个时间循环只有一个 macrotask 任务,可以有多个 microtask任务

来看看上面实例的执行:

  • 首先,setTimeout 被推进到 macrotask 队列(将在下一个macrotask中执行)中。

  • 接着, 会先执行 macrotask 中的第一个任务(整个 script中的同步代码 ),再加上promise 构造函数也是同步的(promise.then 回调被推进到 microtask 队列中),所以会先打印出2 10 3,然后继续执行末尾的,打印出 a0, a1, a2 5

  • 此时,已经执行完了第一个 macrotask , 所以接下来会顺序执行所有的 microtask, 也就是 promise.then的回调函数,从而打印出4。

    此时,microtask 队列中的任务已经执行完毕,所以执行剩下的 macrotask 队列中的任务,也就是 setTimeout, 所以打印出 1.

最终得出的结论是: 同步代码(包括promise的构造函数) -> promise.then -> setTimeout]

参考文章:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop

http://www.ruanyifeng.com/blog/2014/10/event-loop.html

https://juejin.im/entry/5779bb0ac4c971005572ba29

https://www.zhihu.com/question/36972010/answer/71338002

https://developer.mozilla.org/zh-CN/docs/Web/API/Window/setTimeout

https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

https://stackoverflow.com/questions/38752620/promise-vs-settimeout