Skip to content

关于 await

约 917 字大约 3 分钟

JavaScript

2025-03-31

我们在处理 Promise 结果时通常会用 .then() 获取结果,有时为了简化会用 await ,那么 await 是什么?为什么 await 能够简化这个步骤?

await 是基于 Promise 和生成器函数(Generator)的语法糖。它简化了异步代码的编写方式,但底层仍然依赖于 Promise 的机制。

1. await 的本质

1.1 awaitPromise 的语法糖

JavaScript 中, async/await.then() 是用于处理 异步操作 的两种不同方式。 它们都基于 JavaScriptPromise 对象,但提供了不同的语法和使用体验。 async/awaitES2017(ES8) 引入的 语法糖, 用于简化异步代码的写法,使其看起来更像同步代码。

  • 传统 Promise 链

    fetchData()
      .then(data => {
        console.log("数据:", data);
        return processData(data);
      })
      .then(result => {
        console.log("处理结果:", result);
      })
      .catch(error => {
        console.error("错误:", error);
      });
  • 使用 await

    async function handleData() {
      try {
        const data = await fetchData(); // 等待 Promise 完成
        console.log("数据:", data);
        const result = await processData(data);
        console.log("处理结果:", result);
      } catch (error) {
        console.error("错误:", error);
      }
    }

对比
await 将异步代码的写法从链式调用转换为同步风格,但底层仍然是通过 Promisethen 方法实现的。


1.2 async 函数返回 Promise

  • 一个 async 函数会自动返回一个 Promise,即使你直接返回普通值:

    async function getValue() {
      return 42; // 自动包装为 Promise.resolve(42)
    }
    
    getValue().then(value => console.log(value)); // 输出 42
  • 如果函数内有 await,函数会暂停执行直到 Promise 完成:

    async function asyncFunc() {
      console.log("开始");
      await new Promise(resolve => setTimeout(resolve, 1000)); // 暂停 1 秒
      console.log("结束"); // 1 秒后输出
    }

2. 底层机制

2.1 生成器(Generator)与 yield

async/await 的实现借鉴了生成器(Generator)的 yield 语法。例如:

  • 生成器函数

    function* gen() {
      const data = yield fetchData(); // 暂停执行,等待外部输入
      console.log(data);
    }
  • async/await 的等价转换

    async function asyncFunc() {
      const data = await fetchData(); // 等价于生成器的 yield
      console.log(data);
    }

区别
生成器需要手动控制迭代器(next()),而 async/await 自动处理异步流程。


2.2 事件循环与非阻塞

拓展阅读:JavaScript 事件循环机制

  • await 不会阻塞线程
    await 只是让当前 async 函数暂停执行,但 JavaScript 的事件循环仍然可以处理其他任务(如 I/O、定时器)。

  • 底层实现
    当遇到 await 时,JavaScript 引擎会:

    1. await 后的代码注册到微任务队列。
    2. 释放当前线程,执行其他任务。
    3. Promise 完成后,将后续代码加入微任务队列。

3. 与 Promise 的关系

3.1 await 必须配合 Promise

  • await 后面的表达式必须是 Promise(如果不是,会被自动包装为 Promise):
    async function example() {
      const value = await 42; // 等价于 await Promise.resolve(42)
      console.log(value); // 输出 42
    }

3.2 错误处理

  • try/catch 实际上捕获的是 Promise 的拒绝状态:
    async function handleData() {
      try {
        await Promise.reject("出错了"); // 抛出错误
      } catch (error) {
        console.error(error); // 捕获到 "出错了"
      }
    }

4. 为什么说它是语法糖?

4.1 等价转换示例

以下两段代码是等价的:

使用 async/await

async function fetchAndProcess() {
  const data = await fetchData();
  return processData(data);
}

Promise 实现

function fetchAndProcess() {
  return fetchData()
    .then(data => {
      return processData(data);
    });
}

4.2 核心优势

  • 可读性:避免多层嵌套的 .then() 链。
  • 错误处理:用 try/catch 统一处理同步和异步错误。
  • 逻辑直观:代码执行顺序与书写顺序一致。

5. 总结

  • awaitPromise 的语法糖:它简化了异步代码的写法,但底层依赖于 Promise 和微任务队列。
  • 非阻塞特性await 暂停当前函数,但不会阻塞其他操作。
  • 适用场景:需要顺序执行多个异步操作时,或需要同步风格的错误处理。