eventloop的学习总结
初见事件循环
说到事件循环就是现如今的前端大环境来讲是一个老生常谈的话题了,我第一次听到这个概念的时候,是在去年考完插本考试之后找工作的面试题看到的,现在就来给大家总结记录一下
前置知识
在具体说事件循环之前我们先说一个操作系统的相关知识,那就是进程和线程的关系,我们在操作系统上开启一个应用程序的时候,就意味着我们开启了一个进程。
我们也可以理解为进程是线程的容器。那么我们大家都知道JavaScript这门语言是单线程的(但是浏览器是多进程的),他也有自己的进程容器,那就是node和浏览器,简单来说,JavaScript是依赖node和浏览器来去实现执行一些异步任务,例如定时器,ajax,DOM点击事件,不然JavaScript的单线程机制会导致在执行代码的时候造成阻塞的现象
但是node的事件循环和浏览器的事件循环是不一样的,这里我主要说浏览器的事件循环
浏览器的事件循环
这里我简单举个例子
let i = 2;
console.log("定时器开启之前: " + i);
setTimeout(() => {
i = 3
}, 1000);
console.log("定时器开启之后: " + i);
执行结果如下:
执行结果是这样的,从这里可以看到我们的代码是在浏览器的事件循环机制中,没有等待定时器的执行结束才输出i的值,而是直接按照浏览器的事件循环机制顺序输出
那么,浏览器的事件循环机制到底是什么呢,用我自己理解的就是,当我们执行js代码的时候浏览器会把我们的同步任务放在主线程中执行,把我们的异步任务(例如DOM的点击事件,ajax请求,setTimeout,setInterval),会先放到函数调用栈中,等主线程的同步任务都执行完在去看调用栈是否有代码,有就按顺序执行。
准确来说浏览器的事件循环队列不是只有一条,分为微任务队列和宏任务队列,执行顺序是先执行主线程的代码执行完,微任务,微任务队列为空之后再执行宏任务
微任务队列(microtask queue): Promise的then回调、 Mutation Observer API、queueMicrotask()等
宏任务队列(macrotask queue): ajax、setTimeout、setInterval、DOM监听、UI Rendering等
话不多说,我们直接上代码
setTimeout(function () {
console.log("set1");
new Promise(function (resolve) {
resolve();
}).then(function () {
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then4");
});
console.log("then2");
});
});
new Promise(function (resolve) {
console.log("pr1");
resolve();
}).then(function () {
console.log("then1");
});
setTimeout(function () {
console.log("set2");
});
console.log(2);
queueMicrotask(() => {
console.log("queueMicrotask1")
});
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then3");
});
执行结果如下:
Tip: Promise.then()里面的代码是主线程直接执行的代码
这里我来大概来解释一下执行顺序,
- 输出set1的定时器放入宏任务队列
- new Promise里面执行输出pr1(直接执行输出),resolve函数执行,将then1放入微任务队列
- 输出set2的定时器,将定时器再次放入宏任务队列当中
- log函数直接执行输出2
- 开启一个微任务输出queueMicrotask1,放入微任务队列
- 最后一个new Promise执行了resolve函数把then3放入微任务队列
此时主线程代码只输出了pr1, 2,主线程代码执行完了,开始执行微任务队列,此时的微任务状态应该是这样的
按顺序输出then1, queueMicrotask1, then3
微任务执行完了,开始执行宏任务,宏任务此时的状态是这样的
直接输出set1
resolve函数执行,将then4放入微任务队列,直接输出then2
此时主线程输出了set1, then2
微任务和宏任务队列的状态:
主线程执行完,执行微任务,输出then4,微任务这时执行完了就执行宏任务,输出了set2,也就是上面的结果
相信到这里,大家对浏览器的事件循环机制已经有一定的了解