【机制】 JavaScript的事件循环机制总结 eventLoop

0、从个例子开始

//code-01
console.log(1)
setTimeout(() => {
 console.log(2);
});
console.log(3);

稍微有点前端经验的人都知道这段代码输出的应该是 1 3 2,因为setTimeout函数是异步执行。
那么都说js语言是单线程的,就是说是一件事从头到尾做完,那么它是怎么做到异步的呢?
这就要说到浏览器的运行机制了。

1、浏览器的基本机制

浏览器进程大致分为如下几个部分:

我们所说的js单线程,是指浏览器的js引擎线程只有一个,用来执行js的代码,而浏览器的定时触发器线程事件触发器线程结合,可以实现js语言的异步逻辑。那么js到底是怎么执行异步操作的呢?
我们来看下面这张流程图:

2、js语言事件循环机制-基础


我们来根据以上流程图,再来看 上面代码 code-01,
1.代码开始执行,遇到console.log(1) ,打印 1
2.代码继续执行,遇到setTimeout,此为异步任务,交给异步处理模块(这里可能是定时触发器线程),因为没有延迟时间,所以console.log(2)很快加入到了事件队列中,因为同步任务没有执行完,所以现在不能执行
3.代码继续执行,遇到console.log(3),打印 3
4.代码同步任务执行完毕,查看事件队列中是否有任务,发现有console.log(2),于是打印 2

3、js语言事件循环机制-宏任务与微任务

经过上面的分析,我们对 事件循环机制有了初步的了解,现在我们再来看一个例子:

// code-02
console.log(1)
setTimeout(() => {
  console.log(2);
});
new Promise(function(resolve){
    console.log(3)
    resolve()
}).then(function(){
    console.log(4)
})
console.log(5);

上面代码的结果为1 3 5 4 2,
我们知道 promise.then和setTimeout都是异步事件,那为什么then会比setTimeout先执行呢?
其实是因为上面流程图中 事件队列 其实应该分为 宏任务队列微任务队列微任务优先于宏任务,而且要等微任务队列清空,才会去取宏任务队列中的任务。
所以以上流程图应改为:

我们再来根据以上更新的流程图,再来看 上面代码 code-02,
1.代码开始执行,遇到console.log(1) ,打印 1
2.代码继续执行,遇到setTimeout,此为异步任务,交给异步处理模块,因为没有延迟时间,所以console.log(2)很快加入到了宏任务队列
3.代码继续执行,遇到console.log(3),打印 3
4.代码继续执行,遇到then函数,此为异步任务,交给异步处理模块,因为promise马上就resolve,所以console.log(4)很快加入到了微任务队列
5.代码继续执行,遇到遇到console.log(5) ,打印 5
6.代码同步任务执行完毕,查看微任务队列中是否有任务,发现有console.log(4),于是 打印 4
7.微任务队列被清空,查看宏任务队列中是否有任务,发现有console.log(2),于是 打印 2

常见的宏任务
1.script代码(整体的外层代码其实就是第一个宏任务)
2.setTimeout,setInterval,setImmediate
3. i/o事件
4. UI事件,比如点击事件

常见的微任务
1.promise
2.process.nextTick(Node.js)

4、最后一个例子 - 最少延迟时间

我们再来看最后一个例子

  setTimeout(() => {
    console.log(1);
  },2);
  setTimeout(() => {
    console.log(2);
  },1);
  setTimeout(() => {
    console.log(3);
  },0);

运行结果为 2 3 1
可能会有人疑惑,照以上的逻辑,不应该是 3 2 1吗? 这是因为 setTimeout官方给出的规定是:最低延迟为 4ms,(这个有限制条件,但没怎么看懂)
但这个最低时间不同环境好像实现的不太一样
就上面代码而言,在Chorme浏览器中,最低延迟1ms,就是说 0ms 和 1ms 是同样的,
所以根据代码顺序,console.log(2)console.log(3)先进入 宏任务队列

5、总结

  1. js是单线程,只能顺序执行代码, 但是浏览器有其他线程可以处理异步情况
  2. js引擎执行代码时,遇到同步任务则顺序执行,遇到异步任务则交由 异步事件处理模块处理
  3. 异步事件处理模块等事件触发条件达成后,将异步任务分别 加入宏任务队列微任务队列
  4. 同步任务执行完毕后,先执行微任务队列任务,等队列清空时,执行宏任务队列
  5. 每一个宏任务 重复 2 步骤

参考
1.Event Loop的规范和实现
2.这一次,彻底弄懂 JavaScript 执行机制
3.setTimeout和setImmediate到底谁先执行,本文让你彻底理解Event Loop

posted @ 2020-12-10 19:24  木子草明  阅读(236)  评论(0编辑  收藏  举报