JS 事件循环机制之宏任务/微任务;
执行打印顺序:
1 | console.log('script start'); |
我的错误答案是:script start , promise1 , promise2 , script end , setTimeout
正确答案是:script start, script end, promise1, promise2, setTimeout;
因为Promise是微任务,主线程会在同步任务做完后先清空微任务队列,再执行宏任务队列的setTimeout;
宏任务(macro-task)
浏览器为了能够使得 JS 内部 task 与 DOM 任务能够有序的执行,会在一个 task
执行结束后,在下一个 task
执行开始前,对页面进行重新渲染 (task->渲染->task->…)
鼠标点击会触发一个事件回调,需要执行一个宏任务,然后解析 HTMl。
setTimeout的作用是等待给定的时间后为它的回调产生一个新的宏任务。
这就是为什么打印 setTimeout
在 script end
之后。因为打印 script end
是第一个宏任务里面的事情,而 setTimeout
是另一个独立的任务里面打印的。
微任务(micro-task)
微任务通常来说就是需要在当前 task
执行结束后立即执行的任务,比如对一系列动作做出反馈,
或或者是需要异步的执行任务而又不需要分配一个新的 task
,这样便可以减小一点性能的开销。
只要执行栈中没有其他的 js 代码正在执行且每个宏任务执行完,微任务队列会立即执行。
如果在微任务执行期间微任务队列加入了新的微任务,会将新的微任务加入队列尾部,之后也会被执行。
微任务包括了mutation 和 observe的回调还有接下来的例子promise的回调。
一旦一个pormise有了结果,或者早已有了结果(有了结果是指这个promise到了 fulfilled
或 rejected
状态),
他就会为它的回调产生一个微任务,这就保证了回调异步的执行即使这个promise早已有了结果。
所以对一个已经有了结果的promise调用.then(yey, nay)会立即产生一个微任务。
这就是为什么 promise1
, promise2
会打印在 script end
之后,因为所有微任务执行的时候,当前执行栈的代码必须已经执行完毕。
promise1
, promise2
会打印在 setTimeout
之前是因为所有微任务总会在下一个宏任务之前全部执行完毕。