async函数

async 函数

async() : Generator 函数的语法糖。(async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。

async和await两种语法结合可以让异步代码像同步代码一样

// -返回值:promise对象
// -promise对象的结果,由async函数执行的返回值决定
async function fn() {
    // return 'hello world';
    // return返回的结果如果不是一个promise类型的对象(例如:字符串),那么函数的返回结果就是一个成功的promise对象
    // return返回的结果是一个promise对象,那么对象的结果由该对象决定
    return new Promise((resolve, reject) => {
        resolve('成功的数据');
        // reject('失败的数据');
    })
}
var result = fn();
console.log(result); // Promise {<pending>}

await

// -await必须写在async函数中
// -await右侧的表达式一般为promise对象
// -await返回的是promise成功的值
// -await的promise失败了,就会抛出异常,需要通过try...catch捕获处理

async函数对 Generator 函数的改进,体现在以下四点。

(1)内置执行器。

Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。

asyncReadFile();

(2)更好的语义。

asyncawait,比起*号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

(3)更广的适用性。

co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。

(4)返回值是 Promise。

async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。

进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

基本用法

async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

下面是一个例子,指定多少毫秒后输出一个值。

function timeout(ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
}

async function asyncPrint(value, ms) {
    await timeout(ms);
    console.log(value);
}

asyncPrint('hello world', 50);

上面代码指定 50 毫秒以后,输出hello world

由于async函数返回的是 Promise 对象,可以作为await命令的参数。所以,上面的例子也可以写成下面的形式。

async function timeout(ms) {
  await new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint('hello world', 50);

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 () => {};

注意:

1、async函数在声明形式上和普通函数没有区别,函数声明式,函数表达式,对象方法,class方法和箭头函数等都可以声明async函数。

2.任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。

3.async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。

语法

async函数的语法规则总体上比较简单,难点是错误处理机制。

async函数返回一个 Promise 对象。

async函数内部return语句返回的值,会成为then方法回调函数的参数。

async function foo() {
    return 'hello world';
}
foo().then(v => console.log(v)); // hello world

async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。

async function foo() {
    throw new Error('出错了');
}
foo().then(v => console.log('resolve', v), e => console.log('reject', e)); // reject Error: 出错了

Promise对象的状态变化

 async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。

await命令

正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。

async function f() {
    // 等同于
    // return 123;
    return await 123;
}

f().then(v => console.log(v))
// 123

 await命令后面的 Promise 对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到。

async function f() {
  await Promise.reject('出错了');
}

f()
.then(v => console.log(v))
.catch(e => console.log(e))
// 出错了

任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。

async function f() {
  await Promise.reject('出错了');
  await Promise.resolve('hello world'); // 不会执行
}

有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await放在try...catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。

async function fn() {
    try {
        await Promise.reject('出错了');
    } catch (erro) {}
    return await Promise.resolve('hello world');
}

fn().then(v => console.log(v)); // hello world

另一种方法是await后面的 Promise 对象再跟一个catch方法,处理前面可能出现的错误。

async function fn() {

    await Promise.reject('出错了')
        .catch(e => console.log(e))
    return await Promise.resolve('hello world');
}

fn().then(v => console.log(v));
// 出错了
// hello world

错误处理

如果await后面的异步操作出错,那么等同于async函数返回的 Promise 对象被reject

async function fn() {
    await new Promise((resolve, reject) => {
        throw new Error('出错了');
    });
    return await ('hello world'); // 前面出错后面不会执行
}
fn()
    .then(v => console.log(v))
    .catch(e => console.log(e));
// 出错了

防止出错的方法,也是将其放在try...catch代码块之中。

async function fn() {
    try {
        await new Promise((resolve, reject) => {
            throw new Error('出错了');
        });
    } catch (error) {}
    // await new Promise((resolve, reject) => {
    //     throw new Error('出错了');
    // }).catch(e => console.log(e));

    return await ('hello world'); // 前面代码放入try...catch中处理不会影响后面执行
}
fn()
    .then(v => console.log(v))
    .catch(e => console.log(e));
// hello world

如果有多个await命令,可以统一放在try...catch结构中。

async function main() {
  try {
    const val1 = await firstStep();
    const val2 = await secondStep(val1);
    const val3 = await thirdStep(val1, val2);

    console.log('Final: ', val3);
  }
  catch (err) {
    console.error(err);
  }
}

使用注意点

第一点,前面已经说过,await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try...catch代码块中

async function main() {
    try {
        await somethingThatReturnsAPromise();
    } catch (err) {
        console.log(erro);
    }
}

// 另一种写法
async function main() {
    await somethingThatReturnsAPromise()
        .catch(e => console.log(e));
}

第二点,多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。

// 下面两行代码,getFoogetBar是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo完成以后,才会执行getBar,完全可以让它们同时触发。
let foo = await getFoo(); let bar = await getBar();

下面两种写法,getFoogetBar都是同时触发,这样就会缩短程序的执行时间。

// 写法一
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 dbFuc(db) {
  let docs = [{}, {}, {}];

  // 报错
  docs.forEach(function (doc) {
    await db.post(doc);
  });
}

 

posted @ 2021-08-02 10:54  半白半黑  阅读(581)  评论(0)    收藏  举报