Event Loop由浅入深

1.Event Loop概念介绍

JavaScript是一门单线程语言,细分了两个任务种类:同步任务,异步任务。

同步任务:可以被主线程立马执行的任务

异步任务:不进⼊主线程,⽽是进⼊任务队列的任务,执行完毕之后会产生一个回调函数,并且通知主线程。当主线程上的任务执行完后,就会调取最早通知自己的回调函数,使其进入主线程中执行。也就是异步任务会等待同步任务执行完毕后再去执行。

异步任务种类:

(1)Promise.then() ——微任务

(2)async/await ——–微任务

(3)setTimeout() ——–宏任务

(4)setUnterval() ———-宏任务

其余的js宏任务还有:Ajax、DOM事件,文件操作

js微任务有:process.nextTick、MutationObserver、 catch finally

2.微任务与宏任务概念介绍

异步任务可分为微任务和宏任务。在异步任务中,有些异步任务的平均执行周期很长,这些任务被javascript标记为宏任务(比如setTimeout())。而平均执行周期相对比较短的任务,被javascript标记为微任务(比如promise.then())。

3.事件循环机制

异步任务被押入异步任务队列后,会被分为宏任务和微任务。在所有同步任务执行完毕之后,异步任务会优先执行所有已经存在任务队列中的微任务。在所有的微任务执行完毕之后,再去宏任务队列中执行一个宏任务,执行完一个宏任务之后会再去微任务队列中检查是否有新的微任务,有则全部执行,再回到宏任务队列执行一个宏任务,以此循环。这一套流程,就是事件循环(event loop)

4.习题练习

(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log('1');
function promiseFn() {
return new Promise((resolve, reject) => {
  setTimeout(()=> {
    console.log('2');
  })
  resolve('3');
  console.log('4')
})
}

promiseFn().then(res => {
console.log(res);
});

输出结果:1 4 3 2;console.log(‘1’)是同步任务先执行,调用函数promiseFn,new Promise是同步任务,继续往下执行,setTimeout是宏任务,暂时放入异步任务队列中;resolve(‘3’)返回成功的res(resolve在异步操作成功后执行,并将参数传递出去),resolve触发then,打印3(res)

(2)

1
2
3
4
5
6
7
8
9
10
11
new Promise((resolve,reject) => {
setTimeout(() => {
console.log("setTimeout");
resolve("success");
})
}).then(res => console.log(res))
console.log("同步");

// 同步
// setTimeout
// success

由于resolve()在setTimeout中被执行,才触发then(),所以success就会被推到了下一次的事件循环。”success”最后才会被打印。

(3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//第一个宏任务
setTimeout(() => {
console.log(1); //宏任务中的同步任务
Promise.resolve().then(() => { console.log(7) }) //宏任务中的微任务
}, 0); //异步任务 - 宏任务

console.log(2); //同步任务

Promise.resolve().then(() => { console.log(3) }) //异步任务 - 微任务

//第二个宏任务
setTimeout(() => {
console.log(8); //宏任务中的同步任务
setTimeout(() => { console.log(5) }, 0) //宏任务中的宏任务 第四个宏任务
}, 0);

//第三个宏任务
setTimeout(() => {
Promise.resolve().then(() => { console.log(4) }) //宏任务中的微任务
}, 0);

console.log(6); //同步任务
//2 6 3 1 7 8 4 5

首先,同步任务必定优先于所有所有异步任务并按顺序执行。所以输出 2 6。
然后同一批次中剩下一个微任务和一个三个宏任务。
因为宏任务必定会在同一批次环境中的微任务全部执行完毕后再执行,所以场上当前批次中唯一一个微任务先执行。输出3
还剩下三个宏任务。执行第一个宏任务,宏任务中有一个同步任务和一个异步任务。这里要注意两点。
1.统一批次宏任务中按顺序执行
2.一次只执行一个宏任务,然后同步任务当场执行。微任务压入队列。然后就要去检查有没有微任务,有则执行
所以,第一个宏任务执行的时候,产生了一个同步任务和一个微任务。需要注意,宏任务一次只执行一个。执行完之后发现同步任务当场执行(输出1),然后查看微任务队列中有没有微任务可以执行。发现有,则执行微任务(输出7)
然后,才开始执行第二个宏任务。执行第二个宏任务产生了一个同步任务,同步任务当场执行(输出8),产生一个宏任务(宏任务压入任务执行队列,也就是所有宏任务之后)5放在最后面,按事件循环,再次检查是否存在未执行的微任务,发现没有,不执行。
然后执行第三个宏任务,第三个宏任务中产生一个微任务,按事件循环,再去寻找是否存在未执行的微任务,发现有,则执行(输出4)
最后执行第四个宏任务(第二个宏任务产生的)。走一遍事件循环的流程,输出5

补充:await会阻塞后面代码的执行,遇到多个await,会先执行第一个await,其余放入微任务。