async函数是Generator 函数的语法糖。async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。
async
函数对 Generator
函数的改进,体现在以下四点。
- 内置执行器。 调用了asyncReadFile函数,然后它就会自动执行,输出最后结果。也就是说,async函数的执行,与普通函数一模一样,只要一行。
- 更好的语义。 async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
- 更广的适用性。 await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。
- 返回值是 Promise。 async函数的返回值是 Promise 对象,进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。
# 1、Async的语法
1、async函数返回一个 Promise 对象。
async函数内部return语句返回的值,会成为then方法回调函数的参数。async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。
async function f() {
return 'hello world';
}
f().then(v => console.log(v))
// "hello world"
async function f() {
throw new Error('出错了');
}
f().then(
v => console.log(v),
e => console.log(e)
)
2
3
4
5
6
7
8
9
10
11
12
2、Promise对象的状态变化。
async
函数返回的 Promise
对象,必须等到内部所有await
命令后面的 Promise
对象执行完,才会发生状态改变,除非遇到return
语句或者抛出错误。也就是说,只有async
函数内部的异步操作执行完,才会执行then
方法指定的回调函数。
# 2、Await命令
正常情况下,await
命令后面是一个 Promise
对象,返回该对象的结果。如果不是 Promise
对象,就直接返回对应的值。
async function f() {
// 等同于
// return 123;
return await 123;
}
f().then(v => console.log(v))
// 123
2
3
4
5
6
7
另一种情况是,await命令后面是一个thenable对象(即定义then方法的对象),那么await会将其等同于 Promise 对象。
# 3、错误处理
如果await后面的异步操作出错,那么等同于async函数返回的 Promise 对象被reject。
async function f() {
await new Promise(function (resolve, reject) {
throw new Error('出错了');
});
}
f()
.then(v => console.log(v))
.catch(e => console.log(e))
// Error:出错了
2
3
4
5
6
7
8
9
# 4、使用注意点
1) await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try...catch代码块中。
async function myFunction() {
try {
await somethingThatReturnsAPromise();
} catch (err) {
console.log(err);
}
}
// 另一种写法
async function myFunction() {
await somethingThatReturnsAPromise()
.catch(function (err) {
console.log(err);
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
2) 多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;//直接返回
let bar = await barPromise;
2
3
4
5
6
7
3) await命令只能用在async函数之中,如果用在普通函数,就会报错。
async function dbFuc(db) {
let docs = [{}, {}, {}];
// 报错
docs.forEach(function (doc) {
await db.post(doc);
});
}
2
3
4
5
6
7
如果确实希望多个请求并发执行,可以使用Promise.all方法。
async function dbFuc(db) {
let docs = [{}, {}, {}];
let promises = docs.map((doc) => db.post(doc));
let results = await Promise.all(promises);
console.log(results);
}
2
3
4
5
6
4) async 函数可以保留运行堆栈。
const a = () => {
b().then(() => c());
};
2
3
当b()运行的时候,函数a()不会中断,而是继续执行。等到b()运行结束,可能a()早就运行结束了,b()所在的上下文环境已经消失了。如果b()或c()报错,错误堆栈将不包括a()。
const a = async () => {
await b();
c();
};
2
3
4
b()运行的时候,a()是暂停执行,上下文环境都保存着。一旦b()或c()报错,错误堆栈将包括a()。
# 5、实例:按顺序完成异步操作
async function logInOrder(urls) {
for (const url of urls) {
const response = await fetch(url);
console.log(await response.text());
}
}
2
3
4
5
6
上面代码的问题是所有远程操作都是继发。只有前一个 URL 返回结果,才会去读取下一个 URL,这样做效率很差,非常浪费时间。
async function logInOrder(urls) {
// 并发读取远程URL
const textPromises = urls.map(async url => {
const response = await fetch(url);
return response.text();
});
// 按次序输出
for (const textPromise of textPromises) {
console.log(await textPromise);
}
}
2
3
4
5
6
7
8
9
10
11
虽然map方法的参数是async函数,但它是并发执行的,因为只有async函数内部是继发执行,外部不受影响。
# 6、异步遍历器
异步遍历器的最大的语法特点,就是调用遍历器的next方法,返回的是一个 Promise 对象。
asyncIterator
.next()
.then(
({ value, done }) => /* ... */
);
2
3
4
5
# 7、异步 Generator 函数
语法上,异步 Generator 函数就是async函数与 Generator 函数的结合。
async function* gen() {
yield 'hello';
}
const genObj = gen();
genObj.next().then(x => console.log(x));
// { value: 'hello', done: false }
2
3
4
5
6
异步 Generator
函数内部,能够同时使用await
和yield
命令。可以这样理解,await
命令用于将外部操作产生的值输入函数内部,yield
命令用于将函数内部的值输出。
async await
的源码实现,本质上是promise和generator的语法糖
//gen是一个generator函数
const AsyncFunction=gen=>{
return new Promise((resolve,reject)=>{
const iterator=gen();
function next(x){
let {
value,//存储每一次发送请求的promise实例
done //记录是否都迭代完成
}=iterator.next(x);
if(done) {
resolve(value);
return value;
}
if(!isPromise(value)){
value= Promise.resolve(value);
}
value.then(res=>next(res)).catch(err=>{
reject(err);
err=>iterator.throw(err);
})
}
next();
})
}
function isPromise(x){
if(x!==null && /^(object|function)$/i.test(typeof x)){
let then;
try{
then=x.then;
}catch(err){
return false;
}
if(typeof then ==='function'){
return true;
}
}
return false;
}
function query(interval) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(interval);
}, interval);
});
}
function* generator() {
let value;
value = yield query(1000);
console.log("第一个请求", value);
value = yield query(2000);
console.log("第一个请求", value);
value = yield query(3000);
console.log("第一个请求", value);
}
AsyncFunction(generator).then(res=>{
console.log("请求成功");
}).catch(err=>{
console.log("请求失败")
})
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