async

184 阅读4分钟
  • async函数,它就是Generator函数的语法糖

优化和改进

  1. 内置执行器
  • async函数的执行,与普通函数一模一样,只要一行,不像Generator函数,需调用next方法
  1. 更好的语义
  • async和await,比起星号和yield,语义更清楚了.async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果.
  1. 更广的适用性.
  • async函数的await命令后面,可以是Promise对象和原始类型的值(数值,字符串和布尔值,但这时等同于同步操作)
  1. 返回值是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); // 输出 一个async函数f
})
  • 上面代码中,函数f内部return命令返回的值,会被then方法回调函数接收到.

抛出错误

  • async函数内部抛出错误,会导致返回的Promise对象变为reject状态.抛出的错误对象会被catch方法回调函数接收到.
async function f2() {
    throw new Error('测试抛出错误');
}
f2().then(data => {
    console.log(data);
}).catch(error => {
    console.log(error); // 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); // 非Promise
})
  • 上面代码中,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函数之中,如果在普通函数,就会报错