为何会写这篇博文呢?javascript
前段时间,和头条的小伙伴聊天问头条面试前端会问哪些问题,他称若是是他面试的话,event-loop确定是要问的。那天聊了蛮多,event-loop算是给我留下了很深的印象。缘由很简单,由于以前我从未深刻了解过,若是是面试的时候,我遇到了这个问题,估计回答得确定不如人意。html
所以,最近我阅读了一些相关的文章,并细细梳理了一番,输出了本篇博文,但愿能帮助你们搞懂浏览器的event-loop。后续会继续补充node中的event-loop。前端
更多文章可戳: github.com/YvetteLau/B…java
JavaScript的运行机制:node
(1)全部同步任务都在主线程上执行,造成一个执行栈(execution context stack)。git
(2)主线程以外,还存在"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。github
(3)一旦"执行栈"中的全部同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,因而结束等待状态,进入执行栈,开始执行。面试
(4)主线程不断重复上面的第三步shell
归纳便是: 调用栈中的同步任务都执行完毕,栈内被清空了,就表明主线程空闲了,这个时候就会去任务队列中按照顺序读取一个任务放入到栈中执行。每次栈内被清空,都会去读取任务队列有没有任务,有就读取执行,一直循环读取-执行的操做segmentfault
一个事件循环中有一个或者是多个任务队列
JavaScript中有两种异步任务:
宏任务: script(总体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering
微任务: process.nextTick(Nodejs), Promises, Object.observe, MutationObserver;
主线程从"任务队列"中读取执行事件,这个过程是循环不断的,这个机制被称为事件循环。此机制具体以下:主线程会不断从任务队列中按顺序取任务执行,每执行完一个任务都会检查microtask队列是否为空(执行完一个任务的具体标志是函数执行栈为空),若是不为空则会一次性执行完全部microtask。而后再进入下一个循环去任务队列中取下一个任务执行。
详细说明:
执行进入microtask检查的的具体步骤以下:
须要注意的是:当前执行栈执行完毕时会马上先处理全部微任务队列中的事件, 而后再去宏任务队列中取出一个事件。同一次事件循环中, 微任务永远在宏任务以前执行。
图示:
先看一个简单的示例:
setTimeout(()=>{
console.log("setTimeout1");
Promise.resolve().then(data => {
console.log(222);
});
});
setTimeout(()=>{
console.log("setTimeout2");
});
Promise.resolve().then(data=>{
console.log(111);
});
复制代码
思考一下, 运行结果是什么?
运行结果为:
111
setTimeout1
222
setTimeout2
复制代码
咱们来看一下为何?
咱们来详细说明一下, JS引擎是如何执行这段代码的:
再思考一下下面代码的执行顺序:
console.log('script start');
setTimeout(function () {
console.log('setTimeout---0');
}, 0);
setTimeout(function () {
console.log('setTimeout---200');
setTimeout(function () {
console.log('inner-setTimeout---0');
});
Promise.resolve().then(function () {
console.log('promise5');
});
}, 200);
Promise.resolve().then(function () {
console.log('promise1');
}).then(function () {
console.log('promise2');
});
Promise.resolve().then(function () {
console.log('promise3');
});
console.log('script end');
复制代码
思考一下, 运行结果是什么?
运行结果为:
script start
script end
promise1
promise3
promise2
setTimeout---0
setTimeout---200
promise5
inner-setTimeout---0
复制代码
那么为何?
咱们来详细说明一下, JS引擎是如何执行这段代码的:
由于 JavaScript 是单线程的。单线程就意味着,全部任务须要排队,前一个任务结束,才会执行后一个任务。若是前一个任务耗时很长,后一个任务就不得不一直等着。为了协调事件(event),用户交互(user interaction),脚本(script),渲染(rendering),网络(networking)等,用户代理(user agent)必须使用事件循环(event loops)。
最后有一点须要注意的是:本文介绍的是浏览器的Event-loop,所以在测试验证时,必定要使用浏览器环境进行测试验证,若是使用了node环境,那么结果不必定是如上所说。
最后,若是您以为本篇博文给了您一点帮助或者启发,请帮忙点个Star吧~ github.com/YvetteLau/B…
推荐关注本人公众号