宏任务和微任务

参考

微任务、宏任务与 Event-Loop

宏任务和微任务到底是什么?

JavaScript 中的 Event Loop(事件循环)机制

什么是 Event-Loop

JS 是单线程执行的,所以要执行多任务的话依赖于异步和事件循环(Event Loop)。JS 是单线程的,指的是 JS 引擎线程。在浏览器环境中,有 JS 引擎线程和渲染线程,且这两个线程互斥。node 环境中,只有 JS 引擎线程。

JS 引擎常驻内存,等待宿主(浏览器或 node)分配宏任务,反复等待-执行即为事件循环。

Event Loop 中,每次循环称为 tick,每次 tick 的任务如下:

  1. 读宏任务队列,执行其同步代码直至结束,这个过程中可能会有宏任务和微任务入队
  2. 读微任务,并执行,直至微任务队列为空
  3. 如果宿主为浏览器,可能会渲染页面

ES6 规范中,microtask 称为 jobs,macrotask 称为 task。宏任务是由宿主发起的,而微任务由 JavaScript 自身发起。

在 ES3 以及以前的版本中,JavaScript 引擎自身没有发起异步请求的能力,也就没有微任务的存在。在 ES5 之后,JavaScript 引入了 Promise,这样,不需要浏览器,JavaScript 引擎自身也能发起异步任务了。

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// async function async1() {
// console.log('async1 start'); // 2
// await async2();
// console.log('async1 end'); // 7
// }

// async function async2() {
// console.log('async2 start'); // 3
// return new Promise((resolve, reject) => {
// console.log('async2 promise'); // 4
// resolve();
// })
// }

function sync1(){
return new Promise((resolve, reject)=>{
console.log('async1 start'); // 2
return new Promise((resolve, reject) => {
console.log('async2 start'); // 3
return new Promise((resolve, reject) => {
console.log('async3 start'); // 4
resolve();
}).then(()=>{
console.log('async3 end'); // 7
resolve();
})
}).then(() => {
console.log('async2 end'); // 9
resolve();
})
}).then(()=>{
console.log('async1 end'); // 10
})
}

console.log('script start'); // 1

setTimeout(function() {
console.log('setTimeout'); // 11
}, 0);

sync1();

new Promise(function(resolve) {
console.log('promise1 start'); // 5
resolve();
}).then(function() {
console.log('promise1 end'); // 8
})

console.log('script end'); // 6

// script start
// async1 start
// async2 start
// async3 start
// promise1 start
// script end
// async3 end
// promise1 end
// async2 end
// async1 end
// setTimeout
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
new Promise(resolve => {
setTimeout(()=>{
console.log(1);
new Promise(resolve => {
resolve();
})
.then(() => {console.log(2);})
})
resolve();
})
.then(() => {
console.log('then1')
let a = new Promise(resolve => {
resolve();
})
.then(() => {console.log(3);})
Promise.resolve().then(() => {console.log(4);})
.then(() => {console.log(5);})
.then(() => {console.log(6);})
console.log(7);
})
.then(() => {
console.log('then2')
new Promise((resolve) => {
resolve()
})
.then(() => {
console.log(8);
new Promise((resolve) => {
resolve()
})
.then(() => {console.log(9)})
})
.then(() => {
console.log(10);
})
})
.then(() => {
console.log('then3')
console.log(11);
})
.then(() => {
console.log('then4')
console.log(12);
})
// then1
// 7
// 3
// 4
// then2
// 5
// 8
// then3
// 11
// 6
// 9
// 10
// then4
// 12
// 1
// 2
  1. setTimeout回调函数是宏任务,Promise.then的回调函数是微任务
  2. new Promise的 executor 函数是同步执行的
  3. then需等new Promise的 executor 函数执行完,或上一个 then 执行完,才入队