async函数
含义
async 函数是什么?一句话,它就是 Generator 函数的语法糖
//依次读取两个文件
var asyncReadFile = async function () {
var f1 = await readFile('/etc/fstab');
var f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
//一比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已
async
函数对 Generator 函数的改进
(1)内置执行器:Generator 函数的执行必须靠执行器,所以才有了co
模块,而async
函数自带执行器。
var result = asyncReadFile()
//调用了asyncReadFile函数,然后它就会自动执行,输出最后结果。这完全不像 Generator 函数,需要调用next方法,或者用co模块,才能真正执行,得到最后结果。
(2)更广的适用性:co
模块约定,yield
命令后面只能是 Thunk 函数或 Promise 对象,而async
函数的await
命令后面,可以是Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)
(3)返回值是 Promise:async
函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then
方法指定下一步的操作
基本用法
async
函数返回一个 Promise 对象,可以使用then
方法添加回调函数。当函数执行的时候,一旦遇到await
就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
async function getStockPriceByName(name) {
var symbol = await getStockSymbol(name);//依次自动执行,返回一个promise对象
var stockPrice = await getStockPrice(symbol);
return stockPrice;//return返回的值会成为then方法的回调参数
}
getStockPriceByName('goog').then(function (result) {//result上面返回的值
console.log(result);
});
async 函数有多种使用形式
// 函数声明
async function foo() {}
// 函数表达式
const foo = async function () {};
// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)
// Class 的方法
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage = new Storage();
storage.getAvatar('jake').then(…);
// 箭头函数
const foo = async () => {};
语法
-
async函数返回一个 Promise 对象。async函数内部return语句返回的值,会成为then方法回调函数的参数
async function f() { return 'hello world'; } f().then(v => console.log(v)) //返回值“hello world”被当做参数 // "hello world"
-
async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到
async function f() { throw new Error('出错了'); } f().then( v => console.log(v),//resoleved状态时的回调 e => console.log(e)//reject状态时的回调 这里调用这个 ) // Error: 出错了
-
Promise 对象的状态变化
只有
async
函数内部的异步操作执行完,(或者遇到return
语句或者抛出错误,才会执行then
方法指定的回调函数 -
await 命令
正常情况下,
await
命令后面是一个 Promise 对象。如果不是,会被转成一个立即resolve
的 Promise 对象async function f() { return await 123;//await后时数值123,它被转成 Promise 对象,并立即resolve } f().then(v => console.log(v)) // 123
await
命令后面的 Promise 对象如果变为reject
状态,则reject
的参数会被catch
方法的回调函数接收到。只要一个await
语句后面的 Promise 变为reject
,那么整个async
函数都会中断执行async function f() { await Promise.reject('出错了'); } f() .then(v => console.log(v)) .catch(e => console.log(e)) // 出错了 //注意,这里await语句前面没有return,但是reject方法的参数依然传入了catch方法的回调函数。加上return效果也一样
如果希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个
await
放在try...catch
结构里面async function f() { try { await Promise.reject('出错了'); } catch(e) {} return await Promise.resolve('hello world'); } f() .then(v => console.log(v)) // hello world
另一种方法是
await
后面的 Promise 对象再跟一个catch
方法,处理前面可能出现的错误async function f() { await Promise.reject('出错了') .catch(e => console.log(e)); return await Promise.resolve('hello world'); } f() .then(v => console.log(v)) // 出错了 // hello world
-
错误处理
如果
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:出错了
防止出错的方法(防止状态变为reject),就是将其放在
try...catch
代码块之中async function f() { try { await new Promise(function (resolve, reject) { throw new Error('出错了'); }); } catch(e) { } return await('hello world'); } //如果有多个await命令,可以统一放在try...catch结构中 async function main() { try { var val1 = await firstStep(); var val2 = await secondStep(val1); var val3 = await thirdStep(val1, val2); console.log('Final: ', val3); } catch (err) { console.error(err); } }
注意
-
await
命令后面的Promise
对象,运行结果可能是rejected
,所以最好把await
命令放在try...catch
代码块中 -
多个
await
命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。//原始是继发关系,耗时==>由于这两个都是独立的异步操作,最好写成同步触发 let foo = await getFoo(); let bar = await getBar(); // 写法一 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 函数的实现原理就是将 Generator 函数和自动执行器,包装在一个函数里。