基本概述
ES2017 标准引入了 async
函数,它就是 Generator
函数的语法糖。
async
函数就是将 Generator
函数的星号(*
)替换成async
,将yield
替换成await
,仅此而已
async
和Generator
相比:
-
内置执行器,不需要调用
next
方法,不需要co
模块 -
更好的语义,
async
表示函数里有异步操作,await
表示紧跟在后面的表达式需要等待结果。 -
更广的适用性,
async
函数的await
命令后面,可以是 Promise 对象和原始类型的值 -
async
函数的返回值是Promise
对象,可以使用then
方法添加回调函数
函数执行的时候,一旦遇到await
就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
多种使用形式:
// 函数声明
async function foo() {}
// 函数表达式
const foo = async function () {};
// 对象的方法
let obj = { async foo() {} };
// Class 的方法
class Storage {
// ...
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
// 箭头函数
const foo = async () => {};
async
函数内部return
语句返回的值,会成为then
方法回调函数的参数
async
函数内部抛出错误,那么返回的Promise
对象就会走catch()
只有async
函数内部的异步操作执行完,才会执行then
方法指定的回调函数,除非遇到return
或者有错误
await 命令
await
命令后面如果不是 Promise 对象,就直接返回对应的值。
await
命令后面是一个thenable
对象等同于 Promise 对象。
await
命令后面的 Promise 对象如果变为reject
状态,则reject
的参数会被catch
方法中的回调函数接收到
任何一个await
语句后面的 Promise 对象变为reject
状态,那么整个async
函数都会中断执行
处理await
后面的Promise
的reject
,可以使用try...catch
也可以使用catch()
使用注意点
-
最好把
await
命令放在try...catch
代码块中 -
多个
await
命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。这里如果不理解,只需要知道一句话即可,
Promise
新建后会立即执行,所以在使用await
之前,Promise
已经执行了。// 写法一 let [foo, bar] = await Promise.all([getFoo(), getBar()]); // 写法二 let fooPromise = getFoo(); let barPromise = getBar(); let foo = await fooPromise; let bar = await barPromise;
-
await
命令只能用在async
函数之中,如果用在普通函数,就会报错 -
async
函数可以保留运行堆栈,查看原文
async
函数其实内部就是将Generator
函数和自动执行器包装在同一个函数里。
async function fn(args) {
// ...
}
// 等同于
function fn(args) {
return spawn(function* () {
// ...
});
}
与其他异步处理方法的比较
Promise
,会有一堆的then
和catch
,语义不容易看出来
Generator
写法又缺少自动执行器。
async
函数简洁,符合语义,又不需要用户自己写自动执行器。