JavaScript运行机制:Event Loop
Posted 战场小包
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript运行机制:Event Loop相关的知识,希望对你有一定的参考价值。
学习
javascript
执行机制能更好的理解JavaScript
的代码执行顺序,进而更好的理解JavaScript
的异步模式。
概述
在刚开始学习JavaScript
时,我经常会产生两个问题:
JavaScript
是一门单线程语言,那如何实现异步任务?- 同步任务和异步任务的执行顺序如何?
以一个常见的面试题总结上述两个问题:
// 下面代码的打印结果?
console.log("first");
setTimeout(() => {
console.log("second");
},0)
console.log("last");
单线程却可以异步?
首先来解决第一个问题,JavaScript
的确是一门单线程语言,但是浏览器UI
是多线程的,异步任务大多借助浏览器和JavaScript
的执行机制实现。
例如,setTimeout
就借助浏览器定时触发器的计时功能来实现。
浏览器的主要子线程:
GUI
线程、JS
引擎线程、事件触发线程、定时触发器线程、异步http
请求线程
关于JavaScript
的执行顺序,就要进入本文的核心:Event Loop
。
理解Event Loop
Event Loop
过程
JavaScript
将任务分为同步任务和异步任务,同步任务进入主线中中,异步任务首先到Event Table
进行回调函数注册。- 当异步任务的触发条件满足,将回调函数从
Event Table
压入Event Queue
中。 - 主线程里面的同步任务执行完毕,系统会去
Event Queue
中读取异步的回调函数。 - 只要主线程空了,就会去
Event Queue
读取回调函数,这个过程被称为Event Loop
。
触发条件满足: 举个栗子,
setTimeout(cb, 1000)
,当1000ms
后,就讲cb
压入Event Queue
。
再举个栗子,ajax(请求条件, cb)
,当http
请求发送成功后,cb
压入Event Queue
。
有哪些异步任务会进入 Event Queue
呢?
DOM
事件AJAX
请求- 定时器
setTimeout
和setlnterval
ES6
的Promise
案例解析
经过Event Loop
的学习,引言中的案例结果就可以轻易得出,打印顺序为first,last,second。我们来具体分析一下:
// 下面代码的打印结果?
console.log("first"); // 同步任务 打印 first
setTimeout(() => {
// 异步任务 压入Event Table 4ms之后 cb压入Event Queue
console.log("second");
},0)
console.log("last"); // 同步任务 打印last
// 读取Event Queue 打印second
进阶
学到这里,你真的以为你完全明白JavaScript
的执行机制了吗?下面来看一个案例。
setTimeout(() => {
console.log(1);
}, 0)
new Promise(function(resolve){
console.log(2);
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log(3)
});
console.log(4)
经过JavaScript
执行机制的基本学习,可以很轻松得出案例的打印结果:2,4,1,3。
将案例代码在控制台运行,结果如下图:
小小的脑袋大大的疑惑
很懵,为什么Promise
的callback
先执行,·的callback
再执行?
setTimeout
的最小触发时间为4ms
,难道是setTimeout
满足触发条件之前,Promise
先行满足触发条件?
那很简单,我们来做一个测试,将上述案例的Promise
触发callback的时间延长。
setTimeout(() => {
console.log(1);
}, 0)
new Promise(function(resolve){
console.log(2);
// 增大循环
// 本机测试100000000循环大约为240ms
for(var i = 0; i < 100000000; i++){
i == 99999999 && resolve();
}
}).then(function(){
console.log(3)
});
console.log(4)
// 运行结果 2 4 3 1
结果很明显,Promise
的回调仍然在setTimeout
之前,那么说明JavaScript
的运行机制还未理解完全。
宏任务和微任务
JavaScript
除了广义上将任务划分为同步任务和异步任务,还对异步任务进行了更精细的划分。
history traversal
任务(h5
当中的历史操作)
process.nextTick
(nodejs
中的一个异步操作)
MutationObserver
(h5
里面增加的,用来监听DOM
节点变化的)
不同的任务会进入对应的Event Queue
。由于异步任务进行了更详细的划分,因此,Event Loop
的执行顺序会发生变化。(详情见下图)
当主线程空了之后,script
宏任务结束,主线程会去microtask
的Event Queue
进行读取,microtask
全部读取完毕,再去macrotask
中读取下一个宏任务,不断循环上述过程。
案例分析二
现在再来重新分析假总结处的案例。
以上是关于JavaScript运行机制:Event Loop的主要内容,如果未能解决你的问题,请参考以下文章
JavaScript 运行机制详解:Event Loop——续
JavaScript 运行机制以及Event Loop(事件循环)