- async函数,它就是Generator函数的语法糖
优化和改进
- 内置执行器
- async函数的执行,与普通函数一模一样,只要一行,不像Generator函数,需调用next方法
- 更好的语义
- async和await,比起星号和yield,语义更清楚了.async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果.
- 更广的适用性.
- async函数的await命令后面,可以是Promise对象和原始类型的值(数值,字符串和布尔值,但这时等同于同步操作)
- 返回值是Promise
- async函数的返回值是Promise对象,这比Generator函数的返回值是Iterator对象方便多,可以用then方法指定下一步操作
总结:
- async函数完全可以看作多个异步操作,包装成一个Promise对象,而await命令就是内部then命令的语法糖.
用法
- async函数返回一个Promise对象,可以使用then方法添加回调函数,当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
var fs = require('fs')
var readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function (error, data) {
if (error) reject(error)
resolve(data)
})
})
}
var asyncReadFile = async function () {
var f1 = await readFile('./txt1.json')
var f2 = await readFile('./txt2.json')
console.log(f1.toString())
console.log(f2.toString())
}
var result = asyncReadFile()
console.log(result)
语法
- 返回Promise saync函数返回一个Promise对象 async函数内部return语句返回的值,会成为then方法回调函数的参数
async function f() {
return '一个async函数f';
}
f().then(data => {
console.log(data);
})
- 上面代码中,函数f内部return命令返回的值,会被then方法回调函数接收到.
抛出错误
- async函数内部抛出错误,会导致返回的Promise对象变为reject状态.抛出的错误对象会被catch方法回调函数接收到.
async function f2() {
throw new Error('测试抛出错误');
}
f2().then(data => {
console.log(data);
}).catch(error => {
console.log(error);
})
Promise对象的状态变化
- async函数返回的Promise对象,必须等到内部所以await命令后面的Promise对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误.也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
await 命令
- 正常情况下,await命令后面是一个Promise对象,如果不是,会被转成一个立即resolve的Promise对象
async function f4() {
return await '非Promise';
}
f4().then(data => {
console.log(data);
})
- 上面代码中,await命令的参数是 字符串'非Promise',它被转成Promise对象,并立即resolve.
- await命令后面的Promise对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到.
async function f5() {
return await Promise.reject('错误');
}
f5().then(data => {
console.log(data);
}).catch(error => {
console.log(error);
})
- 注意:上面代码中,await语句前面没有return,但是reject方法的参数依然传入了catch方法的回调函数.如果在await前面加上return效果一样
- 只要有一个await语句后面的Promise变为reject,那么整个async函数都会中断执行.
async function f6() {
await Promise.reject('错误');
await Promise.resolve('正确');
}
f6().then(data => {
console.log(data);
}).catch(error => {
console.log(error);
})
- 上面代码中,第二个await语句是不会执行的,因为第一个await语句状态变成了reject.
- 有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作,这时可以将第一个await放在try...catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行.
async function f7() {
try {
await Promise.reject('错误');
} catch {}
return await Promise.resolve('正确');
}
- 另一种方法是await后面的Promise对象再跟一个catch方法,处理前面可能出现的错误
async function f8() {
await Promise.reject('错误').catch(error => {
console.log(error);
});
return await Promise.resolve('正确');
}
- 如果await后面的异步操作出错,那么等同于async函数返回的Promise对象被reject.
注意点
- await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try...catch代码块中
- 多个await命令后面的异步操作,如果不存在激发关系,最好让他们同时出发;
let aw1 = await get1()
let aw2 = await get2()
let [aw1, aw2] = await Promise.all(get1(), get2())
- await命令只能用在async函数之中,如果在普通函数,就会报错