对于事件循环不知道有多少同学在面试的笔试题被它绕的团团转,作为js的处理事件的重要机制,那么今天我们就从最简单的地方开始,把它拿下
了解事件循环机制
为什么会有事件循环机制,js作为一个单线程的语言,意味着如果出现大量事件等它将会按顺序执行,而如果是这样,就会导致如果里面出现一个复杂的计算,就会影响整个进程。就像你在玩游戏,也并不影响我们同时听音乐
用异步去解决局限性,用事件循环机制去调度处理这个过程,分辨哪些是优先处理,哪些是后续处理。确保所有任务按照合理的顺序执行。
事件循环机制内容
简单来说,事件循环机制包括:执行栈,宏任务队列,微任务队列
将我们的事件任务分为:同步任务,宏任务,微任务
执行过程简单来说:
- 将同步任务在执行栈中执行
- 宏任务微任务放到特定队列,由事件管理器去调整后续
- 同步任务执行完,执行栈为空,事件管理器将微任务放入执行栈执行
- 执行栈空,放入宏任务
- 继续微任务,宏任务依次类推
常见 宏任务:
- setTimeout 和 setInterval
- Ajax请求的回调
- DOM 事件监听回调
- postMessage (H5, 向其它窗口分发异步消息) 等
微任务
- Promise对应的then方法的回调
- async & await :await语句之后的代码是微任务(
await本身不是微任务,只是可以简单一点这么去看)等
案例
多说无益,还是从具体的案例中去了解这个过程吧
可以看到我们下面的代码的简单案例中,已经标注了每一个代码是什么任务,结合上面的执行过程我们可以看出这个任务的整体输出结果就是 : 1 3 5(同步按顺序执行) 4(微任务) 2(宏任务)
// 同步
console.log('1')
// 宏任务
setTimeout(() => {
console.log('2')
}, 0);
new Promise((resolve) => {
// 同步
console.log('3');
resolve();
}).then(() => {
// 微任务
console.log('4');
});
// 同步
console.log('5');
复杂一些的案例,这次并没有加上每个代码是哪种事件,大家可以自己试试自己加上。结果是 2 3 5 1 4
console.log(2)同步、setTimeout(one, 0)宏任务、promies.then的console.log('3');微任务、定时器console.log('4');宏任务、console.log('5');微任务- 知道每一个代码是什么任务,接下来进行分析得出结果
two()执行,输出2.后面没有同步代码了- 执行微任务, 3 5
- 执行宏任务, 1 4
function one() {
console.log('1');
}
function two() {
console.log('2');
setTimeout(one, 0);
Promise.resolve().then(() => {
console.log('3');
});
}
two();
setTimeout(() => {
console.log('4');
}, 0);
Promise.resolve().then(() => {
console.log('5');
});
加入await机制
接下来加入 await机制,来了解await在机制中的一个效果
解释:await关键字的核心功能是暂停async函数的执行。当 JavaScript 引擎遇到await关键字时,它会暂停当前async函数的执行,将执行权交出去,让其他同步任务或者已经准备好的微任务先执行,这也就是为啥我说await之后可以简单理解为一个微任务,有相似之处,但不完全一样。
案例:
- 调用
test函数,先执行two函数的同步,console.log('6')
- 然后执行
return Promise,then回调函数添加到微任务队列(test函数里的await在等待two函数返回的这个Promise,所以此时await会暂停test函数的执行,并将这个Promise的then回调函数(也就是输出1的那个微任务)再次添加到微任务队列(这种重复添加不影响最终执行顺序,只是确保这个微任务会在合适的时候执行)。)
- 执行
new Promise,同步代码console.log('3');,输出3 - 调用
resolve,Promise的then回调函数Promise的then回调函数添加到微任务队列 - 执行同步代码
console.log('5');,输出5 - 开始处理微任务队列,执行的是
two函数返回的Promise的then回调函数,输出1 - 执行
new Promise的then回调函数,输出4 test函数恢复执行阶段,执行await后面的代码,输出2。
async function test() {
await two();
console.log(2);
}
function two() {
// 同步
console.log('6');
// 微任务
return Promise.resolve().then(() => {
console.log('1');
});
}
test();
new Promise((resolve) => {
console.log(3);
resolve();
}).then(() => {
console.log(4);
});
console.log(5);
练练手
async function one() {
console.log('1');
await two();
console.log('2');
}
async function two() {
console.log('3');
return Promise.resolve().then(() => {
console.log('4');
});
}
console.log('5');
setTimeout(() => {
console.log('6');
}, 0);
one();
new Promise((resolve) => {
console.log('7');
resolve();
}).then(() => {
console.log('8');
});
console.log('9');
答案:
5 1 3 7 9 4 8 2 6
const p1 = new Promise((resolve) => {
console.log('1');
resolve();
});
const p2 = p1.then(() => {
console.log('2');
return new Promise((resolve) => {
console.log('3');
resolve();
});
});
p2.then(() => {
console.log('4');
});
setTimeout(() => {
console.log('5');
}, 0);
console.log('6');
答案:1 6 2 3 4 5