美文网首页
event loop

event loop

作者: 隐号骑士 | 来源:发表于2019-03-11 23:24 被阅读0次

下面一段简单的代码

console.log('script start');
setTimeout(function() {
  console.log('setTimeout');
}, 0);
console.log('script end');

执行结果是

script start
script end
setTimeout

为什么定时器即使设置0秒,也会在最后一行代码后执行呢?

执行栈和任务队列

JS 的执行是单线程的

JS 分为同步任务和异步任务

JS 执行栈是JS代码运行的栈,主线程会在其上依次执行代码

异步任务被推入任务队列

当JS执行栈空闲时,主线程读取任务队列,并依次执行它们的回调函数

事件循环.png

JS 执行栈 是先进后出, 任务队列 是先进先出。

不难理解,以上代码片段中,定时器回调函数没有在第一时间执行,而是等到第一次执行栈为空之后再去执行的,所以产生了输出的结果

MacroTask 和 MicroTask

想一想下面代码的执行结果,看起来没那么简单了...

console.log('script start');
setTimeout(function() {
  console.log('setTimeout1');
}, 0);
new Promise((resolve)=>{
  console.log('promise1')
  setTimeout(function() {
    console.log('setTimeout2');
  }, 0);
  resolve()
  console.log('promise2')
}).then(()=>{
  setTimeout(function() {
    console.log('setTimeout3');
  }, 0);
  console.log('then')
})
setTimeout(function() {
  console.log('setTimeout4');
}, 0);
console.log('script end');

执行结果是

script start
promise1
promise2
script end
then
setTimeout1
setTimeout2
setTimeout4
setTimeout3

事件队列中的事件分为两类

MacroTask (tasks): setTimeout, setInterval, setImmediate, Ajax, onClick(), OnLoad()

MicroTask (jobs): process.nextTick, Promises, MutationObserver

tasks and jobs.jpg

流程的细节是:

1 把最早的任务(task A)放入任务队列
2 如果 task A 为null (那任务队列就是空),直接跳到第6步
3 将 currently running task 设置为 task A
4 执行 task A (也就是执行回调函数)
5 将 currently running task 设置为 null 并移出 task A
6 执行 microtask 队列
a: 在 microtask 中选出最早的任务 task X
b: 如果 task X 为null (那 microtask 队列就是空),直接跳到 g
c: 将 currently running task 设置为 task X
d: 执行 task X
e: 将 currently running task 设置为 null 并移出 task X
f: 在 microtask 中选出最早的任务 , 跳到 b
g: 结束 microtask 队列
7 跳到第一步

所以,每次执行一个macrotask就会把当前队列里所有的microtask执行完,某种角度上看,microtask似乎比macrotask具有更高的优先级。

还有一点要注意的,按照以上循环规则,每执行一次macrotask就会触发一次UI渲染检查,所以推荐把会带来UI改动的事件尽量放在microtask中。

相关文章

网友评论

      本文标题:event loop

      本文链接:https://www.haomeiwen.com/subject/gdtwpqtx.html