什么是 Event Loop

Event Loop 即事件循环(事件轮询),是指浏览器或者 Node 的一种解决 JavaScript 单线程运行时不阻塞的一种机制,也就是经常使用的异步远离。

EventLoop 事件循环

掌握了事件循环有助于我们去理解 js 带的运行机制。

事件分类

  • 同步任务
  • 异步任务

同步

代码按照上下文执行顺序一次执行。

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

异步

1
-宏任务 - 微任务;
  • 同步任务 1
  • 异步任务
    • 宏任务 3
      • setTimeout
      • setInterval
      • setImmediate (Node 独有)
      • requestAnimationFrame (浏览器独有)
      • I/O
      • UI rendering (浏览器独有)
    • 微任务 2
      • process.nextTick (Node 独有)
      • Promise
      • Object.observe
      • MutationObserver

浏览器对 JavaScript 代码的执行流程如下

  • 执行代码的同步部分
  • 把微任务队列里的任务全部执行
  • 执行宏任务的第一个任务(执行完有可能产生微任务),如果产生了微任务,将微任务推进微任务队列
  • 全局 Script 代码执行完毕后,调用栈 Stack 会清空;
  • 从微队列 microtask queue 中取出位于队首的回调任务,放入调用栈 Stack 中执行,执行完后 microtask queue 长度减 1;
  • 继续取出位于队首的任务,放入调用栈 Stack 中执行,以此类推,直到直到把 microtask queue 中的所有任务都执行完毕。注意,如果在执行 microtask 的过程中,又产生了 microtask,那么会加入到队列的末尾,也会在这个周期被调用执行;

总结:

  • 微任务队列优先于宏任务队列执行,(宏任务优先级最低)
  • 微任务队列上创建的宏任务会被后添加到当前宏任务队列的尾端,微任务队列中创建的微任务会被添加到微任务队列的尾端。
  • 只要微任务队列中还有任务,宏任务队列就只会等待微任务队列执行完毕后再执行

实操一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const p1 = new Promise((res, rej) => {
console.log("Promise");
res(1);
});

// 第一个宏任务
setTimeout(function () {
console.log(1);
setTimeout(function () {
console.log(2);
p1.then((res) => {
console.log(3);
});
}, 0);
console.log(4);
}, 0);

// 第二个微任务
setTimeout(() => {
process.nextTick(() => {
p1.then((res) => {
console.log(5);
setTimeout(function () {
console.log(6);
p1.then((res) => {
console.log(7);
console.log("2:" + res);
});
console.log(8);
}, 0);
console.log(9);
});
});
});

观察下面代码的执行顺序:

1
2
3
4
5
6
7
8
console.log(1);
console.log(3);
Promise.resolve(4).then((res) => {
console.log(res);
});
setTimeout(() => {
console.log(2);
}, 0);

我们可以得知,先执行同步任务,在执行异步任务。
异步任务当中有宏任务、微任务,先执行微任务

宏任务和微任务的优先级

1
2
3
4
5
6
7
8
console.log(1);
console.log(3);
Promise.resolve(4).then((res) => {
console.log(res);
});
setTimeout(() => {
console.log(2);
}, 0);

我们可以得知,先执行同步任务,在执行异步任务。
异步任务当中有宏任务、微任务,先执行微任务

宏任务嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
console.log(1);
setTimeout(() => {
console.log(7);
setTimeout(() => {
console.log(3);
});
console.log(8);
Promise.resolve(4).then((res) => {
console.log(res);
});
console.log(9);
}, 0);
setTimeout(() => {
console.log(10);
Promise.resolve(5).then((res) => {
console.log(res);
});
console.log(11);
setTimeout(() => {
console.log(6);
});
console.log(12);
});
console.log(2);

// 1 2 789 4 101112 5 3 6