async/await

async/await 是 JavaScript 中处理异步操作的核心语法糖,其底层结合了 生成器(Generator)Promise事件循环(Event Loop) 的机制,实现了异步代码的同步化书写。以下从核心原理、执行流程和底层机制三个层面展开解析:

一、核心原理:语法糖背后的 Promise 与协程

1. async 函数的本质

  • 返回 Promiseasync 函数始终返回一个 Promise 对象。即使函数内部返回非 Promise 值,也会被自动包装为 Promise.resolve(value)

    javascript

    javascript 复制代码
    async function foo() { return 1 }
    console.log(foo()); // Promise { <resolved>: 1 }
  • 异常处理 :函数内部抛出的错误会被包装为 Promise.reject(error),可通过 try/catch.catch() 捕获。

2. await 的暂停与恢复机制

  • 非阻塞暂停await 会暂停当前 async 函数的执行,但不阻塞主线程。后续代码会被封装为微任务,等待右侧 Promise 解决后恢复执行。

    javascript

    javascript 复制代码
    async function bar() {
      console.log(1);
      await Promise.resolve(); // 暂停,后续代码进入微任务队列
      console.log(2);
    }
    bar();
    console.log(3); // 输出顺序:1 → 3 → 2
  • 自动 Promise 化 :若 await 后接非 Promise 值,会自动包装为 Promise.resolve(value)

    javascript

    csharp 复制代码
    const result = await 42; // 等价于 await Promise.resolve(42)

二、执行流程:事件循环与微任务的协作

1. 事件循环的调度逻辑

  • 宏任务(MacroTask)与微任务(MicroTask)

    • 宏任务 :包括 setTimeoutsetInterval、I/O 操作、UI 渲染等。
    • 微任务 :包括 Promise.then()MutationObserverawait 后的代码。
  • 执行顺序

    1. 执行当前宏任务中的同步代码。
    2. 清空微任务队列(按注册顺序执行)。
    3. 执行下一个宏任务,循环往复。

2. await 的微任务化过程

  • await 右侧的 Promise 进入 fulfilled 状态时,await 会将后续代码封装为微任务,并通过事件循环调度执行。

    javascript

    javascript 复制代码
    async function example() {
      console.log('A');
      await new Promise(resolve => setTimeout(resolve, 0));
      console.log('B');
    }
    example();
    console.log('C');
    // 输出顺序:A → C → B(因为 B 是微任务,在当前宏任务结束后执行)

3. 错误处理的集中化

  • try/catch 可捕获 await 后的 Promise 拒绝状态,避免分散的 .catch() 调用。

    javascript

    javascript 复制代码
    async function fetchData() {
      try {
        const response = await fetch('invalid-url');
        const data = await response.json();
      } catch (error) {
        console.error('Error:', error);
      }
    }

三、底层机制:生成器与状态机的结合

1. 生成器的暂停与恢复

  • 协程模型 :生成器函数通过 yield 关键字实现暂停和恢复,async/await 利用这一特性将异步操作分段执行。

    javascript

    javascript 复制代码
    function* generator() {
      const a = yield Promise.resolve(1);
      const b = yield Promise.resolve(2);
      return a + b;
    }

2. 执行器的自动化驱动

  • co 库的启示 :早期需手动编写执行器驱动生成器,而 async/await 由引擎自动实现。执行器的核心逻辑是递归处理 yield 的 Promise,并通过 .then() 恢复生成器。

    javascript

    scss 复制代码
    function co(gen) {
      return new Promise((resolve, reject) => {
        const g = gen();
        function next(value) {
          try {
            const { done, value: res } = g.next(value);
            if (done) resolve(res);
            else Promise.resolve(res).then(next, reject);
          } catch (err) {
            reject(err);
          }
        }
        next();
      });
    }

3. V8 引擎的优化

  • 状态机实现async 函数被编译为状态机,通过 SwitchOnGeneratorState 指令管理执行位置。await 会生成 AsyncFunctionAwaitUncaught 内部方法,处理 Promise 的解决与恢复。
  • 微任务队列优化 :V8 在处理 await 时直接复用 Promise 实例,避免创建临时 Promise,提升性能。

四、常见误区与最佳实践

1. 误区澄清

  • await 不阻塞主线程await 仅暂停当前 async 函数,其他任务(如定时器、UI 渲染)仍可执行。
  • 顺序执行的本质 :多个 await 的顺序执行依赖事件循环的调度,实际是异步操作的序列化。

2. 最佳实践

  • 并发处理 :使用 Promise.all() 并行执行独立的异步操作。

    javascript

    scss 复制代码
    const [res1, res2] = await Promise.all([fetch(url1), fetch(url2)]);
  • 避免 forEach 中的 awaitforEach 无法感知异步,应使用 for...of 循环。

    javascript

    scss 复制代码
    for (const item of items) {
      await process(item); // 正确顺序执行
    }

五、总结

async/await 通过 生成器的协程机制 实现代码的分段执行,利用 Promise 的状态管理 封装异步操作,最终借助 事件循环的微任务调度 实现非阻塞的 "同步" 执行。其核心优势在于:

  • 代码可读性:异步逻辑更接近自然语言。

  • 错误处理 :集中式的 try/catch 替代分散的 .catch()

  • 性能优化:与 Promise 性能接近,避免回调地狱的额外开销。

理解这一机制后,开发者可更高效地编写异步代码,同时规避常见的执行顺序陷阱。

文章来源豆包!

相关推荐
科技快报4 小时前
全新尚界H5凭借HUAWEI XMC数字底盘引擎技术,让湿滑路面也“稳”操胜券
华为·harmonyos
猫林老师5 小时前
实战:基于HarmonyOS 5构建分布式聊天通讯应用
分布式·wpf·harmonyos
2501_919749037 小时前
鸿蒙:使用animation或animateTo实现图片无限旋转效果
华为·harmonyos
安卓开发者8 小时前
鸿蒙NEXT分布式文件系统:开启跨设备文件访问新时代
华为·harmonyos
程序员潘Sir10 小时前
鸿蒙应用开发从入门到实战(十四):ArkUI组件Column&Row&线性布局
harmonyos·鸿蒙
阿里云云原生16 小时前
移动端性能监控探索:鸿蒙 NEXT 探针架构与技术实现
华为·架构·harmonyos
351868039919 小时前
鸿蒙音乐应用开发:打造跨设备的音乐体验
华为·harmonyos
351868039919 小时前
鸿蒙音乐应用开发:音频播放与UI交互实战
华为·音视频·交互·harmonyos·arkts