理解async和await

本文深入探讨了ES7引入的Async/Await特性,对比了传统异步编程方法,如回调和Promise,展示了Async/Await如何简化异步代码,使其更接近同步代码的书写方式。文章还介绍了Async函数的返回值处理、错误捕捉机制及项目中如何配置使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、先简单的介绍一下async和await

ES7 提出的async 函数,终于让 JavaScript 对于异步操作有了终极解决方案。async 函数是 Generator 函数的语法糖。使用 关键字 async 来表示,在函数内部使用 await 来表示异步。

  • async/await 是一种编写异步代码的新方法。之前异步代码的方案是回调和 promise。
  • async/await 是建立在promise 的基础上
  • async/await 像 promise 一样,也是非阻塞的。
  • async/await让异步代码看起来、表现起来更像同步代码。

async和await还有一个很有意思的语法规定,就是这两个会同时出现,也就是说await只能出现在async函数中。
那么这里我们抛出一个疑问:那这个 async 函数应该怎么调用?

1.async怎么处理返回值

async 函数负责返回一个 Promise 对象
如果在async函数中 return 一个直接量,async 会把这个直接量通过Promise.resolve() 封装成 Promise 对象;
如果 async 函数没有返回值,它会返回 Promise.resolve(undefined)

async function testAsync() {
    return "hello async";
}
const result = testAsync();
console.log(result);

//输出结果: Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "hello async"}

可以看出:async 函数返回的是一个 Promise 对象。 如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。

如果没有返回值:

async function testAsync1() {
    console.log("hello async");
}
let result1 = testAsync1();
console.log(result1);

//输出结果: Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: undefined}

结果返回Promise.resolve(undefined)。

所以在没有 await 的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,并且不会阻塞后面的语句。这和普通返回 Promise 对象的函数并无二致。

await 在等待什么

一般我们都用await去等带一个async函数完成,不过按语法说明,await 等待的是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值,所以,await后面实际可以接收普通函数调用或者直接量>

如果await等到的不是promise对象,那跟着的表达式的运算结果就是它等到的东西;
如果是promise对象,await会阻塞后面的代码,等promise对象resolve,得到resolve的值作为await表达式的运算结果虽然await阻塞了,但await在async中,async不会阻塞,它内部所有的阻塞都被封装在一个promise对象中异步执行。

2.async/await 优势

来一个例子比较一下then()链和async/awai:
执行三个步骤,每一个步骤都需要之前步骤的结果。

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(m, n) {
    console.log(`step2 with ${m} and ${n}`);
    return takeLongTime(m + n);
}

function step3(k, m, n) {
    console.log(`step3 with ${k}, ${m} and ${n}`);
    return takeLongTime(k + m + n);
}

1.先用 .then()的方法

function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => {
            return step2(time1, time2)
                .then(time3 => [time1, time2, time3]);
        })
        .then(times => {
            const [time1, time2, time3] = times;
            return step3(time1, time2, time3);
        })
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

doIt();

看起来就非常复杂!不仅要处理一堆的参数,还非常容易在逻辑上出错。

2.用 async/await 来写:

async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time1, time2);
    const result = await step3(time1, time2, time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}

doIt();

这样逻辑就非常清晰了,浅显易懂,还便于维护!

二、Async 函数的错误处理

async 函数的语法不难,难在错误处理上。
先来看下面的例子:

let a;
async function f() {
    await Promise.reject('error');
    a = await 1; // 这段 await 并没有执行
}
f().then(v => console.log(a));

如上面所示,当 async 函数中只要一个 await 出现 reject 状态,则后面的 await 都不会被执行。
解决办法:可以添加 try/catch。

// 正确的写法
let a;
async function correct() {
    try {
        await Promise.reject('error')
    } catch (error) {
        console.log(error);
    }
    a = await 1;
    return a;
}

correct().then(v => console.log(a)); // 1

如果有多个 await 则可以将其都放在 try/catch 中。

三、那怎么样在项目中使用呢

依然是通过 babel 来使用。
只需要设置 presets 为 stage-3 即可。
安装依赖:

npm install babel-preset-es2015 babel-preset-stage-3 babel-runtime babel-plugin-transform-runtime

修改.babelrc:

"presets": ["es2015", "stage-3"],
"plugins": ["transform-runtime"]

这样就可以在项目中使用 async 函数了。

四、总结

使用 async / await, 搭配 promise, 可以通过编写形似同步的代码来处理异步流程, 提高代码的简洁性和可读性。
Async Await 的优点:

  1. 解决了回调地狱的问题
  2. 支持并发执行
  3. 可以添加返回值 return xxx;
  4. 可以在代码中添加try/catch捕获错误
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值