Zhi-Ling

You can do Everything you want

0%

EventLoop

Brief Intro: JavaScript is a single thread lang, it can only do one thing at one tiem. Because of event loop, js thread isn’t blocked while running into asnychronous events.

  • Event Loop
  • Browser Event
  • Node Event

事件循环

js 是单线程,任务分为同步和异步
异步情形:读取文件、网络请求等

异步任务回调通知

  • 等待异步任务准备的同时,js 去执行其他同步任务,等待准备好了再去执行功能,也叫非阻塞式
  • 实现 “通知” 的则是就是时间循环,它是计算机的一种机制
  • 事件循环是由一个队列组成,异步任务回到先进先出,跟据队列任务的不同,分为宏任务和微任务

浏览器事件循环

由一个宏任务队列 + 多个微任务队列组成

  • 第一个宏任务:全局 script 脚本。产生的宏任务和微任务进入各自队列中,执行完 script 后,清空当前微任务队列,完成一次事件循环;
  • 再取出另外一个宏任务,把回调放入队列中,再清空微任务队列
  • 宏任务队列只有一个,每一个宏任务都有自己的微任务队列,每轮循环都是由一个宏任务 + 多个微任务组成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Promise.resolve().then(() => {
console.log("第一个回调函数:微任务1");
setTimeout(() => {
console.log("第三个回调函数:宏任务2");
}, 0);
});
setTimeout(() => {
console.log("第二个回调函数:宏任务1");
Promise.resolve().then(() => {
console.log("第四个回调函数:微任务2");
});
}, 0);
// 第一个回调函数:微任务1
// 第二个回调函数:宏任务1
// 第四个回调函数:微任务2
// 第三个回调函数:宏任务2

Node 事件循环

由 6 个宏任务队列 + 6 个微任务队列组成

  • 一个宏任务队列全部完成后,清空一次微任务队列,然后到下一个等级的宏任务队列,以此往复
  • 一个宏任务队列搭配一个微任务队列,六个等级的宏任务全部完成后,才是一轮循环
  • node 端宏微任务也有优先级先后:先 process.nextTick,后 promise.then 等
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
35
console.log("Script开始");
setTimeout(() => {
console.log("第一个回调函数,宏任务1");
Promise.resolve().then(function () {
console.log("第四个回调函数,微任务2");
});
}, 0);
setTimeout(() => {
console.log("第二个回调函数,宏任务2");
Promise.resolve().then(function () {
console.log("第五个回调函数,微任务3");
});
}, 0);
Promise.resolve().then(function () {
console.log("第三个回调函数,微任务1");
});
console.log("Script结束");

// node端:
// Script开始
// Script结束
// 第三个回调函数,微任务1
// 第一个回调函数,宏任务1
// 第二个回调函数,宏任务2
// 第四个回调函数,微任务2
// 第五个回调函数,微任务3

// 浏览器
// Script开始
// Script结束
// 第三个回调函数,微任务1
// 第一个回调函数,宏任务1
// 第四个回调函数,微任务2
// 第二个回调函数,宏任务2
// 第五个回调函数,微任务3

node11.x 前后版本差异

前:取出一整个宏任务队列中全部任务,然后执行一个微任务队列
后:和浏览器类似,先执行一个宏任务,然后一个微任务队列。依然保留宏任务队列和微任务队列优先级

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
35
36
37
38
39
40
console.log("Script开始");
setTimeout(() => {
console.log("宏任务1(setTimeout)");
Promise.resolve().then(() => {
console.log("微任务promise2");
});
}, 0);
setImmediate(() => {
console.log("宏任务2");
});
setTimeout(() => {
console.log("宏任务3(setTimeout)");
}, 0);
console.log("Script结束");
Promise.resolve().then(() => {
console.log("微任务promise1");
});
process.nextTick(() => {
console.log("微任务nextTick");
});

// node11.x后
// Script开始
// Script结束
// 微任务nextTick
// 微任务promise1
// 宏任务1(setTimeout)
// 微任务promise2
// 宏任务3(setTimeout)
// 宏任务2(setImmediate)

// node11.x前
// Script开始
// Script结束
// 微任务nextTick
// 微任务promise1
// 宏任务1(setTimeout)
// 宏任务3(setTimeout)
// 微任务promise2
// 宏任务2(setImmediate)

宏任务

常见宏任务

  • script(整体代码)
  • setTimout:回调不一定在指定时间后执行,而是把回调放到事件循环队列中等待执行;0ms 默认最小时间为 4ms
  • setInterval
  • setImmediate (node 独有):用来把一些需要长时间运行的操作放在一个回调函数里,在浏览器完成后面的其他语句后,就立刻执行这个回调函数,setTimeout 优先级高于 setImmediate,也可以替代 setTimeout (0)
  • requestAnimationFrame (浏览器独有)
  • IO
  • UI render(浏览器独有)

微任务

  • process.nextTick (node 独有)
  • Promise.then()
  • Object.observe
  • MutationObserver

Welcome to my other publishing channels