async 函数
1.ES2017 标准引入了 async 函数, 它是对 Generator 函数的改进 , 我们先看一个读取文件的例子:
Generator 写法是这样的 :
- var fs = require('fs');
- var readFile = function (fileName) {
- return new Promise(function (resolve, reject) {
- fs.readFile(fileName, function(error, data) {
- if (error) return reject(error);
- resolve(data);
- });
- });
- };
- var gen = function* () {
- var f1 = yield readFile('/etc/fstab');
- var f2 = yield readFile('/etc/shells');
- console.log(f1.toString());
- console.log(f2.toString());
- };
async 写法如下 :
- 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, 同时不需要 co 模块, 更加语义化;
2.async 函数返回一个 Promise 对象, 内部 return 语句返回的值, 会成为 then 方法回调函数的参数;
- async function f() {
- return 'hello world';
- }
- f().then(res => console.log(res))
- // "hello world"
3.async 函数返回的 Promise 对象, 必须等到内部所有 await 命令后面的 Promise 对象执行完, 才会发生状态改变, 除非遇到 return 语句或者抛出错误. 也就是说, 只有 async 函数内部的异步操作执行完, 才会执行 then 方法指定的回调函数;
4. 如果 async 函数内部抛出错误, 会导致返回的 Promise 对象变为 reject 状态, 抛出的错误对象会被 catch 方法回调函数接收到;
- async function f() {
- throw new Error('出错了');
- }
- f().then(
- res => console.log(res),
- err => console.log(err)
- )
- // Error: 出错了
5.await 命令后面是一个 Promise 对象. 如果不是, 会被转成一个立即 resolve 的 Promise 对象
- async function f() {
- return await 'Hello World';
- }
- f().then(res => console.log(res))
- // 'Hello World'
上述代码中, await 命令的参数是'Hello World', 它被转成 Promise 对象, 并立即 resolved
6.await 命令后面的 Promise 对象如果变为 reject 状态, 则 reject 的参数会被 catch 方法的回调函数接收到
- async function f() {
- await Promise.reject('出错了');
- }
- f().then(res => console.log(res))
- .catch(err => console.log(err))
- // 出错了
7.async 函数中 , 只要一个 await 语句后面的 Promise 变为 reject, 那么整个 async 函数都会中断执行;
- async function f() {
- await Promise.reject('出错了');
- await Promise.resolve('hello world'); // 不会执行
- }
- f(); // 出错了
8. 如果我们希望即使前一个异步操作失败, 也不要中断后面的异步操作. 那我们可以将第一个 await 放在 try...catch 结构里面, 这样不管这个异步操作是否成功, 第二个 await 都会执行;
- async function f() {
- try {
- await Promise.reject('出错了');
- } catch(e) {
- }
- return await Promise.resolve('hello world');
- }
- f().then(res => console.log(res))
- // hello world
或者是 await 后面的 Promise 对象再跟一个 catch 方法, 处理前面可能出现的错误:
- async function f() {
- await Promise.reject('出错了')
- .catch(err => console.log(err));
- return await Promise.resolve('hello world');
- }
- f()
- .then(res => console.log(res))
- // 出错了
- // hello world
9. 如果 await 后面的异步操作出错, 那么等同于 asyn c 函数返回的 Promise 对象被 rejected:
- async function f() {
- await new Promise(function (resolve, reject) {
- throw new Error('出错了');
- });
- }
- f().then(res => console.log(res))
- .catch(err => console.log(err))
- // Error: 出错了
10. 为了防止出错, 做法也是将其放在 try...catch 代码块之中, 并且如果有多个 await 命令, 可以统一放在 try...catch 结构中.
- async function f() {
- try {
- await new Promise(function (resolve, reject) {
- throw new Error('出错了');
- });
- } catch(e) {
- }
- return await('hello world');
- }
- f().then( res => console.log(res) )
- // 'hello world'
11. 使用 await 要注意以下几点 :
await 命令后面的 Promise 对象, 运行结果可能是 rejected, 所以最好把 await 命令放在 try...catch 代码块中
- async function myFunction() {
- try {
- await operations();
- } catch (err) {
- console.log(err);
- }
- }
- // 另一种写法
- async function myFunction() {
- await operations()
- .catch(function (err) {
- console.log(err);
- });
- }
多个 await 命令后面的异步操作, 如果不存在继发关系(即互不依赖), 最好让它们同时触发, 缩短程序的执行时间
- // 写法一
- 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 function func(db) {
- let docs = [{}, {}, {}];
- // 报错
- docs.forEach(function (doc) {
- await db.post(doc);
- });
- }
来源: http://www.bubuko.com/infodetail-2593627.html