概述

我们都知道js是一个单线程,里面的的请求方式分为两种,一种是同步请求,一种是异步请求,同步方法先执行完毕,然后再去异步任务队列中查看有没有异步任务,有才会执行。

在JavaScript中,所有的任务都可以分为

同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行

异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等

常见的宏任务

scriptsetTimeoutsetIntervalsetImmediate

常见的微任务

一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前

Promise中的then、async、await、process.nextTick(Node.js)、MutationObserver(监听DOM变化的事件)、Object.observe(已废弃;Proxy 对象替代)

关于两者之间的区别:微任务是批量执行、宏任务则是一个一个的执行。

  • 执行一个宏任务,如果遇到微任务就将它放到微任务的事件队列中
  • 当前宏任务执行完成后,会查看微任务的事件队列,然后将里面的所有微任务依次执行完

这里我们可以看到,script标签包含的下面的代码块,那么这就属于是第一个异步任务,也就是宏任务,首先去执行它

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
console.log('1')
setTimeout(function () {
console.log('2')
});
new Promise(function (resolve) {
console.log('3');
resolve();
}).then(function () {
console.log('4')
setTimeout(function () {
console.log('5')
});
});
new Promise(function (resolve) {
console.log('6');
resolve();
}).then(function () {
console.log('7')
setTimeout(function () {
console.log('8')
});
});
</script>
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
setTimeout(() => {
console.log(0)
}, 0)

new Promise((resolve, reject) => {
console.log(1)
resolve()
}).then(() => {
console.log(2)
new Promise((resolve, reject) => {
console.log(3)
resolve()
}).then(() => {
console.log(4)
}).then(() => {
console.log(5)
})
}).then(() => {
console.log(6)
})

new Promise((resolve, reject) => {
console.log(7)
resolve()
}).then(() => {
console.log(8)
})

172384650
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
console.log(1);
new Promise((resolve, reject) => {
console.log(2);
setTimeout(() = >{
console.log(3)
});
resolve()
}).then(() =>{
console.log(4)
});

setTimeout(() =>{
console.log(5)
});
console.log(6)

126435


<script>
console.log('1')
setTimeout(function () {
console.log('2')
});
new Promise(function (resolve) {
console.log('3');
resolve();
}).then(function () {
console.log('4')
setTimeout(function () {
console.log('5')
});
});
new Promise(function (resolve) {
console.log('6');
resolve();
}).then(function () {
console.log('7')
setTimeout(function () {
console.log('8')
});
});
console.log(9)
</script>

136947258

let promise = new Promise(resolve=>{
setTimeout(()=>{
resolve();
console.log('1')
},0);
console.log('2')
}).then(value=>console.log('3'))
console.log('4')
2413


console.log(1) // 遇到 console.log(1) ,直接打印 1

// 遇到定时器,属于新的宏任务,留着后面执行
setTimeout(()=>{
console.log(2)
}, 0)

// 遇到 new Promise,这个是直接执行的,打印 'new Promise'
new Promise((resolve, reject)=>{
console.log('new Promise')
resolve()
}).then(()=>{ // .then 属于微任务,放入微任务队列,后面再执行
console.log('then')
})

console.log(3) // 遇到 console.log(3) 直接打印 3

// 好了本轮宏任务执行完毕,现在去微任务列表查看是否有微任务,发现 .then 的回调,执行它,打印 'then'
// 当一次宏任务执行完,再去执行新的宏任务,这里就剩一个定时器的宏任务了,执行它,打印 2


实际结果是:1=>'new Promise'=> 3 => 'then' => 2

总结

同步>微任务>宏任务

async与await

async 是异步的意思,await则可以理解为 async wait。所以可以理解async就是用来声明一个异步方法,而 await是用来等待异步方法执行

async函数返回一个promise对象,下面两种方法是等效的

1
2
3
4
5
6
7
8
function f() {
return Promise.resolve('TEST');
}

// asyncF is equivalent to f!
async function asyncF() {
return 'TEST';
}

正常情况下,await命令后面是一个 Promise对象,返回该对象的结果。如果不是 Promise对象,就直接返回对应的值。不管await后面跟着的是什么,await都会阻塞后面的代码

1
2
3
4
5
6
7
8
9
10
11
12
async function fn1 (){
console.log(1)
await fn2()
console.log(2) // 阻塞
}

async function fn2 (){
console.log('fn2')
}

fn1()
console.log(3)

上面的例子中,await会阻塞下面的代码(即加入微任务队列),先执行 async外面的同步代码,同步代码执行完,再回到 async 函数中,再执行之前阻塞的代码

所以上述输出结果为:1,fn2,3,2

参考文章