Event Loop
Event Loop定义了浏览器执行你写的代码的顺序。我们都知道浏览器在执行代码的时候,并不一定按照你写的顺序来执行,因为这里边可能存在异步执行,而且可能有多个异步代码,还有可能有多种异步代码。那么当这种情况存在的时候,浏览器就需要有一种机制,来判断当前应当执行哪部分代码。
然后,还需要知道Event Loop机制中两个任务,第一个是宏任务MacroTask
,第二个是微任务MicroTask
,还有一个叫执行栈
的东西。Event Loop的机制就是先让执行栈执行完宏任务队列
中的所有任务,然后再执行微任务队列
中的所有任务,完了继续循环。
- 执行栈:所有的js代码都会被放到执行栈中依次执行。
- 宏任务:简单点说,就是一串js代码,但是被划分为了宏任务。哪些会被划分为宏任务呢?包括
script标签中代码
setTimeout
setInterval
I/O
UI渲染
postMessage
等(并没有归纳完)。宏任务可以继续产生宏任务/微任务。 - 微任务:同理,也是一串被划分为微任务的js代码。包括
Promise中的resolve/reject
async/await
process.nextTick
等(并没有归纳完)。同样,微任务也可以继续产生宏任务/微任务。
废话少说,赶紧聚个例子:
//先来个简单点的
console.log("start");
setTimeout(_ => console.log("setTimeout"), 0);
console.log("end");
// start
// end
// setTimout
//解释不?算了不解释了
// 第二个例子,还是简单点
console.log("start");
setTimeout(_ => console.log("setTimeout"), 0);
new Promise(resolve => {
console.log("new Promise");
resolve("promise then");
}).then(res => console.log(res));
console.log("end");
// start
// new Promise
// end
//promise then
// setTimeout
//解释不?还是解释哈嘛……
/* 是弄个子的
1.执行栈会首先去拿宏任务列表中的代码块来执行,此时只有script标签代码,所以就拿过来执行咯哟
2.执行第一句,输出 start
3.执行第二句,遇到了setTimeout,此时会将其回掉任务加入到下一轮的宏任务中
4.new Promise中的代码直接执行(为什么直接执行,请看Promise相关知识),
输出 new Promise,完了遇到了resolve代码,此时会将其回掉任务加入到下一轮微任务中
5.输出 end,此时,本轮宏任务执行完毕,开始执行微任务
6.由上可知,微任务中第一条(也仅此一条)是resolve的回掉,所以拿出来执行,输出 promise then
7.微任务执行完了,再次执行宏任务,也从第一条开始,输出setTimeout
8.done!!!
*/
//第三个例子,不难,只是多增加了两位新客人而已
console.log("start");
setTimeout(_ => console.log("setTimeout"), 0);
new Promise(resolve => {
console.log("new Promise");
setTimeout(_ => console.log("new Promise setTimeout"), 0);
resolve("promise then");
}).then(res => console.log(res));
async function asyncFunc1() {
console.log("asyncFunc1 start");
await asyncFunc2();
console.log("asyncFunc1 end");
}
async function asyncFunc2() {
console.log("asyncFunc2");
}
asyncFunc1();
console.log("end");
/* 先补充哈async/await的只是哈 */ /* 开始补充... */ /* 补充完毕 */
//输出
//start
//new Promise
//asyncFunc1 start
//asyncFunc2
//end
//promise then
//asyncFunc1 end
//setTimeout
//new Promise setTimeout
/* 不用解释了,只要晓得 async/await 而且李杰了上一个例子,那就没问题了 */
// 第四个例子
console.log("start");
setTimeout(function setTimeoutCallbackFunc1() {console.log("setTimeout")}, 0);
new Promise(resolve => {
console.log("new Promise");
setTimeout(function setTimeoutCallbackFunc2() {console.log("new Promise setTimeout")}, 0);
resolve("promise then");
}).then(function promiseThenCallbackFunc1(res) {console.log(res)});
async function asyncFunc1() {
console.log("asyncFunc1 start");
await asyncFunc2();
console.log("asyncFunc1 end");
}
async function asyncFunc2() {
console.log("asyncFunc2");
return new Promise(resolve => {
setTimeout(function setTimeoutCallbackFunc3() {
console.log("asyncFunc2 promise");
resolve();
}, 0);
})
}
asyncFunc1();
console.log("end");
//这又输出什么呢?这个可以说一哈,至于输出啥子,稍等哈,我复制到浏览器执行哈...
// 执行完了,输出
// start
// new Promise
// asyncFunc1 start
// asyncFunc2
// end
// promise then
// setTimeout
// new Promise setTimeout
// asyncFunc2 promise
// asyncFunc1 end
/* 终于打完了 */ /* 虽然我在浏览器中执行后照到打的,但是我能解释清楚 */ /* 开始装... */
为了形象点,画图又不会,咋个搞呢?搞个都看得懂的
- macList = [] //宏任务队列
- micList = [] //微任务队列
- 比如:macList = [task1, task2, task3, task4, task5, ...]
好了,开始装了...
- 首先,执行栈还是会先执行宏任务,那就去宏任务列表中取,此时的macList = [script],所以取出script执行,macList = []。
- 输出 start。
- 遇到setTimout,将回掉加入到宏任务列表中,macList = [setTimeoutCallbackFunc1]。
- 执行new Promise代码,输出 new Promise,然后遇到setTimeout,加入到红任务列表中,macList = [setTimeoutCallbackFunc1, setTimeoutCallbackFunc2],然后又遇到resolve,将其回掉加入到微任务列表,micList = [promiseThenCallbackFunc1]。
- (请先预备async/await知识)执行asyncFunc1函数,则输出 asyncFunc1 start,遇到 await asyncFunc2(),则先执行函数,输出asyncFunc2,然后返回了一个new Promise,new Promise中的代码回立马执行,遇到了setTimeout,加入到宏任务队列,macList = [setTimeoutCallbackFunc1, setTimeoutCallbackFunc2, setTimeoutCallbackFunc3],而后返回到asyncFunc1中,后边的代码会全部被封装到then中,直到asyncFunc2中resolve才会被执行。
- 第5点结束,asyncFunc1执行也就结束,然后输出 end。
- 至此,本轮宏任务结束,输出了 start, new Promise, asyncFunc1 start, asyncFunc2, end。开始执行微任务。
- 然后开始微任务列表挨个执行。此时micList = [promiseThenCallbackFunc1],取出promiseThenCallbackFunc1执行,输出 promise then。微任务队列执行完毕,再次执行宏任务。
- 此时宏任务列表macList = [setTimeoutCallbackFunc1, setTimeoutCallbackFunc2, setTimeoutCallbackFunc3]。挨个取出执行。
- 执行setTimeoutCallbackFunc1,输出 setTimout
- 执行setTimeoutCallbackFunc2,输出 new Promise setTimout
- 执行setTimeoutCallbackFunc3,输出 asyncFunc2 promise,但此时又遇到resolve,此时会将await asyncFunc2()后的代码当作回掉,加入到微任务列表中micList = [console.log("asyncFunc1 end")]。宏任务队列又执行完毕,又开始执行微任务。
- 此时微任务micList = [console.log("asyncFunc1 end")],取出执行,输出 asyncFunc1 end。此时全部执行完毕。
done!!!!
理解得不深,如有错误,赶紧指正,非常感谢!!!
网友评论