async 和 await
1.async await含义
async是Generator函数的语法糖
generator: generator返回一个迭代器,每次使用迭代器的next方法执行,每次执行都是执行到下一次的yield
const fs = require('fs');
const readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) return reject(error);
resolve(data);
});
});
};
const gen = function* () {
// yield表达式就是暂停标志
// yield类似于return,但yield是返回本次 de 函数执行的返回值
const f1 = yield readFile('/etc/fstab');
const f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
执行generator
let genIterator = gen();
genIterator.next(); //{value: , done: }
genIterator.next();
将上面的gen函数写成async函数
const asyncReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
async和gen函数之间的区别
- async函数就是将Generator函数的星号(*)替换成async,将yield替换成await
- async内置执行器,async函数的执行与普通函数一模一样,只要一行。而gen函数的执行必须依靠执行器,需要调用next方法才能执行
- async有更好的语义。async表示函数里有异步操作,await表示紧跟在后面的代码需要等待结果
- async函数的返回值是Promise对象,gen函数的返回值是Iterator对象。所以async函数可以使用.then进行回调
2.async基本用法
async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成后,再接着执行函数体内后面的语句
-
async函数返回一个Promise对象
-
async函数内部return语句返回的值,会成为then方法回调函数的参数
async function f() { return 'hello world'; } f().then(v => console.log(v)) // "hello world"
-
async函数内部抛出错误,会导致返回的Promise对象变为reject状态,并且会被catch方法接收
async function f() { throw new Error('出错了'); } f().then().catch((e)=>console.log(e)) //出错了
-
Promise对象的状态变化
- 当遇到return或抛出错误,则从async函数体中跳出,并且如果是抛出错误的跳出则Promise为rejected状态
- 只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
-
await命令后面是一个Promise对象/thenable对象,返回该对象的结果,如果不是Promise对象,就直接返回对应的值
如果await命令后的Promise对象如果变成reject状态,则整个async函数都会中断执行,并且reject的参数会被catch方法的回调函数接收
解决办法:
//方法1 async function f() { //try catch块捕获异常 try { //可写多个await命令 await Promise.reject('出错了'); } catch(e) { } //这样不管上述异步操作是否成功,第二个await都会执行并return return await Promise.resolve('hello world'); } f() .then(v => console.log(v)) // hello world //方法2 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
3.使用注意点
-
await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中,否则会导致跳出代码块
-
多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发
let foo = await getFoo(); //getFoo执行完才能执行getBar let bar = await getBar();
上面代码中,getFoo和getBar是两个独立的异步操作,只有getFoo完成以后,才会执行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 函数可以保留运行堆栈