异步函数 asyncfunction

  • async关键字用于声明一个异步函数:
    • async是asynchronous单词的缩写,异步、非同步;
    • async是synchronous单词的缩写,同步、同时;
  • async异步函数可以有很多中写法:

异步函数的执行流程

  • 异步函数的内部代码执行过程和普通的函数是一致的,默认情况下也是会被同步执行。
  • 异步函数有返回值时,和普通函数会有区别:
    • 情况一:异步函数也可以有返回值,但是异步函数的返回值相当于被包裹到Promise.resolve中;
    • 情况二:如果我们的异步函数的返回值是Promise,状态由会由Promise决定;
    • 情况三:如果我们的异步函数的返回值是一个对象并且实现了thenable,那么会由对象的then方法来决定;
  • 如果我们在async中抛出了异常,那么程序它并不会像普通函数一样报错,而是会作为Promise的reject来传递;

异步函数返回值:

1
2
3
4
5
6
7
8
9
async function foo2() {
// 1.返回一个普通的值
// -> Promise.resolve(321)
return ["abc", "cba", "nba"]
}

foo2().then(res => {
console.log("res:", res)
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function foo2() {

// 2.返回一个Promise
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("aaa")
}, 3000)
})

}

foo2().then(res => {
console.log("res:", res)
})

1
2
3
4
5
6
7
8
9
10
11
12
13
async function foo2() {

// 3.返回一个thenable对象
return {
then: function(resolve, reject) {
resolve("bbb")
}
}
}

foo2().then(res => {
console.log("res:", res)
})

await关键字

  • async函数另外一个特殊之处就是可以在它内部使用await关键字,而普通函数中是不可以的。
  • await关键字有什么特点呢?
    • 通常使用await是后面会跟上一个表达式,这个表达式会返回一个Promise
    • 那么await会等到Promise的状态变成fulfilled状态,之后继续执行异步函数
  • 如果await后面是一个普通的值,那么会直接返回这个值
  • 如果await后面是一个thenable的对象,那么会根据对象的then方法调用来决定后续的值
  • 如果await后面的表达式,返回的Promise是reject的状态,那么会将这个reject结果直接作为函数的Promise的reject值
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
function requestData(url) {
console.log("request data")
return new Promise((resolve) => {
setTimeout(() => {
resolve(url)
}, 3000)
})
}

async function test() {
console.log("test function")
return "test"
}

async function bar() {
console.log("bar function")

return new Promise((resolve) => {
setTimeout(() => {
resolve("bar")
}, 2000);
})
}

async function demo() {
console.log("demo function")
return {
then: function(resolve) {
resolve("demo")
}
}
}


// 2.调用的入口async函数
async function foo() {
console.log("foo function")

const res1 = await requestData("why")
console.log("res1:", res1)

const res2 = await test()
console.log("res2:", res2)

const res3 = await bar()
console.log("res3:", res3)

const res4 = await demo()
console.log("res4:", res4)
}

foo()

宏任务和微任务

  • 但是事件循环中并非只维护着一个队列,事实上是有两个队列:
    • 宏任务队列(macrotask queue):ajax、setTimeout、setInterval、DOM监听、UI Rendering等
    • 微任务队列(microtask queue):Promise的then回调、 Mutation Observer API、queueMicrotask()等
  • 那么事件循环对于两个队列的优先级是怎么样的呢?
    • 1.main script中的代码优先执行(编写的顶层script代码);
    • 2.在执行任何一个宏任务之前(不是队列,是一个宏任务),都会先查看微任务队列中是否有任务需要执行
      • 也就是宏任务执行之前,必须保证微任务队列是空的;
      • 如果不为空,那么就优先执行微任务队列中的任务(回调);
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 start")

setTimeout(function () {
console.log("setTimeout1");
new Promise(function (resolve) {
resolve();
}).then(function () {
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then4");
});
console.log("then2");
});
});

new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("then1");
});

setTimeout(function () {
console.log("setTimeout2");
});

console.log(2);

queueMicrotask(() => {
console.log("queueMicrotask1")
});

new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then3");
});

console.log("script end")

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
console.log("script start")

function requestData (url) {
console.log("requestData")
return new Promise((resolve) => {
setTimeout(() => {
console.log("setTimeout")
resolve(url)
}, 2000);
})
}

// 2.await/async
async function getData () {
console.log("getData start")
const res = await requestData("why")// console.log("getData start") 和 “=”后面的代码同时执行,但不执行“=”前面的代码

console.log("then1-res:", res)// 这些代码和上边“const res”属于一部分,在微队列中
console.log("getData end")
}

getData()

console.log("script end")

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
async function async1 () {
console.log('async1 start')
await async2(); // await的上边代码和自己右边的代码属于一部分,会同时执行。
// 左边的代码会和下面的代码一起执行
console.log('async1 end')
}

async function async2 () {
console.log('async2')
// 这里默认return undefined=>async1函数中的“await async2()”函数
// 会Promise.resolve(undefined),将console.log('async1 end')加入微队列中
}

console.log('script start')

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

async1();

new Promise(function (resolve) {
console.log('promise1')
resolve();
}).then(function () {
console.log('promise2')
})

console.log('script end')