美文网首页
JS事件循环(Event Loop)

JS事件循环(Event Loop)

作者: 高级程序狗 | 来源:发表于2020-08-11 14:43 被阅读0次

本文是近期学习js事件循环后的总结,比较适合入门,但不一定准确,欢迎指正。文章内容大量参考以下博文,非常推荐阅读:

https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
https://juejin.im/post/6844903512845860872
https://github.com/aooy/blog/issues/5
html5 Event Loop定义

Event loop用来协调事件、用户交互、脚本、呈现、网络等,说白了就是控制js什么时候应该做什么事情。需要注意的是,event loop在nodejs和浏览器中处理方式不同,本文特指浏览器环境(一些老版本浏览器也先不提😢)。

猜猜看下面这段代码会输出什么:

console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');

正常情况会按照以下顺序打印:script start, script end, promise1, promise2, setTimeout

在详细解释前,先说明几个概念:

  • 单线程
    js是单线程语言,语句会按照出现的顺序依次执行(web worker会启多个线程,但是每个线程会对应一个event loop,各自独立运行,互不影响)。

  • Task任务
    Task主要包括ajax的回调函数、各种事件、setTimeout 等等。所谓 Task Queue 是指存放任务的队列,队列中的任务会按顺序执行,但是两个任务之间可能穿插其它工作,比如渲染。
    ps: Task 在一些博客中被叫做 MacroTask 宏任务,但是规范中并没有出现 MacroTask 关键字,所以本文还是称为 Task

  • MicroTask微任务
    MicroTask通俗的说就是需要在当前 Task 执行结束后立即执行的任务,主要包括 MutationObserverPromise.thenObject.observeMicroTask Queue是一个与Task Queue相互独立的队列,用来存放 MicroTask。每一个Task中产生的MicroTask都将会添加到MicroTask Queue中,MicroTask中产生的MicroTask也将会添加至当前队列的尾部。

概念比较枯燥,结合一张图来理解:

image.png
图片出处

关于之前的demo,执行过程如下(超详细版):

  • 第一轮:

1、代码被添加到 Task Queue

Task Queue JS Stack MicroTask Queue
script - -

2、JS Stack 获取到 Task Queue 的第一个任务,开始执行(任务执行完成后才会从队列中移除):

Task Queue JS Stack MicroTask Queue
script script -

3、打印 script start
4、执行到 setTimeout 时,将回调添加到 Task Queue

Task Queue JS Stack MicroTask Queue
script, setTimeout script -

5、执行到 Promise.resolve().then 时,将回调添加到 MicroTask Queue

Task Queue JS Stack MicroTask Queue
script, setTimeout script then1

6、打印 script end
7、此时代码执行完成, JS Stack 为空

Task Queue JS Stack MicroTask Queue
script, setTimeout - then1

8、 MicroTask Queue 为空吗?否。所以从 MicroTask Queue 取出 Promise.resolve().then 的回调执行。

Task Queue JS Stack MicroTask Queue
script, setTimeout then1 then1

9、打印 promise1
10、遇到 Promise.resolve().then.then,添加到 MicroTask Queue

Task Queue JS Stack MicroTask Queue
script, setTimeout then1 then1, then2

11、此时代码执行完成, JS Stack 为空

Task Queue JS Stack MicroTask Queue
script, setTimeout - then2

12、MicroTask Queue 为空吗?否。所以从 MicroTask Queue 取出 then2 执行,打印 promise2

Task Queue JS Stack MicroTask Queue
script, setTimeout then2 then2

13、JS StackMicroTask Queue 均为空,本轮结束:

Task Queue JS Stack MicroTask Queue
setTimeout1 - -
  • 第二轮:

1、Task Queue 取出 setTimeout 回调执行,打印 setTimeout

Task Queue JS Stack MicroTask Queue
setTimeout setTimeout -

2、全部执行完毕

Task Queue JS Stack MicroTask Queue
- - -

尝试一下更复杂的例子,分析时记得在本子上写下Task Queue | JS Stack | MicroTask Queue,记录它们的变化:

console.log('1');
setTimeout(function() {
    console.log('2');
    setTimeout(function(){console.log('3')})
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
console.log('6')
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})
setTimeout(function() {
    console.log('9');
    new Promise(function(resolve) {
        setTimeout(function(){console.log('10')})
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})

结果是:1 6 7 8 2 4 5 9 11 12 3 10,轮数过多,这里暂时不做详细解释。

相关文章

网友评论

      本文标题:JS事件循环(Event Loop)

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