前言
创业完全找不到任何出路了,春节又有点遥远,还是得着手准备找工作了😭;
准备面试,顺手写点总结的文章,在梳理思路的同时希望也能为大家提供一点帮助吧。
ps:这个系列会不定期更新
正文
理解起来其实也很简单,Event loop 叫做事件循环,重点是“循环” 二字,对于理解代码执行非常重要。
Event Loop 是什么?为什么?
简单来说就是一句话,因为JS 是单线程的,且自上而下执行,Event Loop 就是用来解决异步事件执行顺序的问题。
当JS 代码执行的时候,会创建一个调用栈,所有JS 代码都在这个调用栈里执行,同时会创建一个宏任务队列和一个微任务队列。
事件循环开始
先检查调用栈是否为空,如果不为空则执行调用栈里的代码;如果为空,则从宏任务队列中取出一个任务并执行。
宏任务
整个js 代码块就是一个宏任务,进入函数体,如果在这个宏任务中创建了微任务,则将微任务加入微任务队列,切记,是加入队列,并没有执行。
常见宏任务:整体代码块、定时器
微任务
宏任务执行完毕后,如果微任务队列不为空,则从微任务队列中取出一个微任务,并执行,这个过程会一直执行,直到微任务队列为空。
常见微任务:promise.then/catch/finally
基本概念概述完毕,
例子一
先看一个简单的例子,解析如果没写明白,请评论区告知。
console.log(1); // 第一次循环宏任务
// 定时器,进入宏任务队列
setTimeout(() => {
console.log(2);
});
// 声明函数
function fn() {
console.log(3);
// 返回promise
return new Promise((resolve) => {
console.log(4);
resolve();
});
}
// 执行函数fn,并且调用promise 的回调函数
// .then,进入微任务队列
fn().then(() => {
console.log(5);
});
理解关键点:
- prmise 的主体是同步的,只有回调函数才是异步的微任务;
- 如果宏任务中创建了微任务,会加入微任务队列;
执行过程:
- 事件循环开始:检查调用栈,如果为空,则从宏任务队列中取出一个任务,并执行。所以第一次获取到的宏任务输出结果就是1 3 4;宏任务队列中有一个定时器,微任务队列中有一个promise 的回调函数;
- 第二次循环,第一步执行完毕了这一次循环的宏任务,然后先检查微任务,直到微任务队列为空;输出结果5
- 继续循环,先获取宏任务,所以输出2
- 直到调用栈为空。
此时再看执行过程是不是很明了?
例子二
将第一个例子改的复杂一点
console.log(1);
// 定时器一
setTimeout(() => {
console.log(2);
});
function fn() {
console.log(3);
return new Promise((resolve) => {
console.log(4);
resolve();
});
}
// 定时器二
setTimeout(() => {
console.log(8);
// promise 回调一
fn().then(() => {
console.log(5);
// promise 回调二
fn().then(() => {
console.log(6);
// 定时器三
setTimeout(() => {
console.log(7);
// promise 回调3
fn().then(() => {
console.log(8);
});
});
});
});
});
执行过程:
- 事件循环开始,调用栈如果为空,则取一个宏任务,当前循环的宏任务就是整体代码块。在这一次循环当中,执行宏任务,输出1 2 8;
- 在定时器二 这个宏任务 中 创建了一个 微任务(promise 回调一),那么将这个微任务加入到微任务队列。
- 宏任务执行完毕后,检查微任务队列,此时微任务队列中只有一个微任务就是 promise 回调一,执行回调,输出 3 4 5,微任务,微任务中又加入了一个微任务(promise 回调二),输出3 4 6,在执行promise 回调二 的时候,又创建了一个宏任务。
- 再次循环,执行宏任务输出7,并且加入了微任务 promise 回调三 ,输出8
- 所以最后输出顺序就是:
-
- 1
- 2 8
- 3 4 5
- 3 4 6
- 7
- 3 4 8
补充说明
还有一个高频问题,宏任务和微任务的优先级,我认为宏任务是优先于微任务的,因为当一次事件循环执行的时候,会先取出一个宏任务,并执行,如果当前这一次的循环创建了一个微任务,仅仅是将微任务加入到微任务队列中,并没有执行,只有当宏任务执行完毕之后,并且微任务队列不为空,那么才会执行微任务,我不理解为什么这么多人说微任务是先与宏任务执行的,这样子似乎搞错了循环这个概念。
拿例子一 来说为什么宏任务是先与微任务执行的。
事件第一次循环:
调用栈为空,则取一个宏任务放入调用栈,执行三个红色框的代码,fn().then 是一个微任务,仅仅是加入微任务队列,没有执行。宏任务执行完毕之后再检查微任务队列,然后将本次循环中的微任务全部执行完。此时调用栈为空。
事件第二次循环:
继续取出宏任务,此时宏任务只有蓝色框的定时器,所以执行定时器的内容。此时微任务队列为空,本次循环执行完毕。
将事件循环拆解开来,按照每一次循环来理解,宏任的优先级就是高于微任务。