理解async和await

    技术2024-10-08  57

    一、先简单的介绍一下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 的优点:

    解决了回调地狱的问题支持并发执行可以添加返回值 return xxx;可以在代码中添加try/catch捕获错误
    Processed: 0.010, SQL: 9