setTimeout(fn 0)与Promise的执行顺序
一道爆款面试题,考察 开发者对于promise 和 setTimeout的执行时间的情况,下面开始:
1 | setTimeout(function() { |
我将面试题常见的关于 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 | new Promise(function(resolve, reject) { |
结果: 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