彻底弄懂 JavaScript 执行机制(Event Loop)
Posted 连先生有猫病
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了彻底弄懂 JavaScript 执行机制(Event Loop)相关的知识,希望对你有一定的参考价值。
创建日期:2020-01-18
背景
作为一枚“前端打字员”, 无论是新手还是老鸟,都会遇到一个令人深省的问题,既然js 是单线程执行的,是按照语句出现的顺序执行的,那么异步的代码 js 是怎么处理的呢?下面的代码是如何进行输出的?
console.log(1);
setTimeout(function() {
console.log(2);
}, 0);
new Promise(function(resolve) {
console.log(3);
resolve(Date.now());
}).then(function() {
console.log(4);
});
console.log(5);
setTimeout(function() {
new Promise(function(resolve) {
console.log(6);
resolve(Date.now());
}).then(function() {
console.log(7);
});
}, 0);
好了,小伙子们可以尽情发挥你的想象力,如果你的答案不是”1, 3, 5, 4, 2, 6, 7“,那你就要认认真真接着往下看了。什么?你竟然答对了。那你更要接着往下看,后面还有好看的彩蛋哦~
同步和异步
首先通过一张导图来看一下javascript事件循环中,同步和异步的关系:
同步和异步任务分别进入不同的执行 process,同步的进入主线程,异步的进入Event Table并注册函数。
当指定的事情完成时,Event Table会将这个函数移入Event Queue。
主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
上述过程会不断重复,也就是常说的Event Loop(事件循环)
宏任务和微任务、时间循环
然而,仅仅理解到此,当然是还不够的:除了广义的同步任务和异步任务,我们对任务有更精细的定义:
macro-task(宏任务):包括整体代码script,setTimeout,setInterval
micro-task(微任务):Promise,process.nextTick
事件循环,宏任务,微任务的关系如图所示:
事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务
回到我们上面说的实例代码:
console.log(1);
setTimeout(function() {
console.log(2);
}, 0);
new Promise(function(resolve) {
console.log(3);
resolve(Date.now());
}).then(function() {
console.log(4);
});
console.log(5);
setTimeout(function() {
new Promise(function(resolve) {
console.log(6);
resolve(Date.now());
}).then(function() {
console.log(7);
});
}, 0);
执行步骤如下:
执行 log(1),输出 1;
遇到 setTimeout,将回调的代码 log(2)添加到宏任务中等待执行;
执行 console.log(3),将 then 中的 log(4)添加到微任务中;
执行 log(5),输出 5;
遇到 setTimeout,将回调的代码 log(6, 7)添加到宏任务中;
宏任务的一个任务执行完毕,查看微任务队列中是否存在任务,存在一个微任务 log(4)(在步骤 3 中添加的),执行输出 4;
取出下一个宏任务 log(2)执行,输出 2;
宏任务的一个任务执行完毕,查看微任务队列中是否存在任务,不存在;
取出下一个宏任务执行,执行 log(6),将 then 中的 log(7)添加到微任务中;
宏任务执行完毕,存在一个微任务 log(7)(在步骤 9 中添加的),执行输出 7;
因此,最终的输出顺序为:1, 3, 5, 4, 2, 6, 7
当然,你可以在Promise.then实现一个稍微耗时的操作(比如 1~1000的for循环),这个步骤看起来会更加地明显。马上会输出1,稍等一会儿才会输出3,然后再输出2。不论等待多长时间输出3,2一定会在3的后面输出。这也就印证了eventloop中的第3步操作,必须等所有的微任务执行完毕后,才开始下一个宏任务
常见的宏任务、微任务
宏任务
setTimeout、setInterval
setImmediate(node.js)
渲染任务、JS脚本执行
IO操作(网络请求、文件读写)
微任务
Promise.resolve()
MutationObserver(web)
process.nextTick(node.js)
requestAnimationFrame(web)
也属于异步执行的方法,但该方法既不属于宏任务,也不属于微任务, 具体参考MDN中的定义
总结
javascript是一门单线程语言
Event Loop是 javascript的执行机制
最后, 希望大家早日实现:成为前端高手的伟大梦想!!!
欢迎交流~
作者个人微信:wanderlian
本文版权归原作者(曜灵)所有!未经允许,严禁转载!对非法转载者, 原作者保留采用法律手段追究的权利!
以上是关于彻底弄懂 JavaScript 执行机制(Event Loop)的主要内容,如果未能解决你的问题,请参考以下文章