源本科技 | 码上会

JavaScript Async Await

2026/03/13
3
0

学习目标

  • 理解 asyncawait 的核心概念,学会编写类同步风格的异步代码。

  • 掌握 async 函数的特性:始终返回 Promise 以及自动包装非 Promise 返回值。

  • 熟练使用 await 关键字暂停执行以等待 Promise 结算,消除回调嵌套。

  • 运用 try...catch 块优雅地处理异步操作中的错误,替代繁琐的 .catch() 链。


Async/Await

在 JavaScript 中,Async/Await 是建立在 Promise 之上的语法糖。它允许开发者以同步代码的写法来处理异步逻辑,极大地提升了代码的可读性、可理解性和可维护性。

核心优势

  • 代码清晰: 避免了 .then().catch() 的链式调用,逻辑流程一目了然。

  • 错误处理简化: 可以使用传统的 try...catch 语句捕获异步错误。

  • 非阻塞执行: 虽然写法像同步,但底层依然是非阻塞的,不会卡住主线程。

  • 结构化流程: 非常适合管理复杂的异步业务逻辑(如顺序依赖的多个 API 请求)。

基础示例

async function fetchData() {
  try {
    // 模拟一个已解决的 Promise ( mock 数据)
    const response = await Promise.resolve({
      json: async () => ({
        userId: 1,
        id: 1,
        title: "示例文章",
        body: "这是用于演示 async/await 的模拟数据"
      })
    });

    // 等待 json() 方法解析完成
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("获取数据时出错:", error);
  }
}

fetchData();

Async

什么是 Async 函数

async 关键字用于声明一个异步函数。它让基于 Promise 的代码看起来像同步代码,同时保持执行的非阻塞特性。

核心特性

  1. 始终返回 Promise:

    • 无论函数内部返回什么值,async 函数都会自动将其包装在一个 Promise 中。

    • 如果返回的是普通值,等价于 Promise.resolve(value)

    • 如果返回的是一个已被拒绝的 Promise 或抛出错误,等价于 Promise.reject(error)

  2. 非阻塞: 函数内部的 await 只会暂停该函数内部的执行,不会阻塞浏览器的其他操作(如 UI 渲染、其他事件响应)。

  3. 无缝配合 Await: 只有在 async 函数内部才能使用 await 关键字。

语法与行为

// 声明方式 1: 函数声明
async function myFunction() {
  return "Hello"; // 实际返回 Promise.resolve("Hello")
}

// 声明方式 2: 箭头函数
const getData = async () => {
    let data = "Hello World";
    return data; // 实际返回 Promise.resolve("Hello World")
};

// 调用验证
getData().then(data => console.log(data)); 
// 输出: Hello World

注意: 即使你直接 return "字符串",外部调用时也必须使用 .then()await 来获取值,因为返回值本质上是一个 Promise。


Await 关键字

什么是 Await

await 关键字用于暂停 async 函数的执行,直到等待的 Promise 对象结算(resolved 或 rejected)。

核心规则

  • 仅限内部使用: await 只能在 async 函数内部使用。在全局作用域或非 async 函数中使用会报错(注:现代浏览器顶部层级支持 Top-level Await,但在函数体内仍是主流用法)。

  • 暂停而非阻塞: 它暂停的是当前 async 函数的后续代码执行,让出主线程去处理其他任务,直到 Promise 有结果后恢复执行。

  • 获取结果: await 表达式的值就是 Promise resolve 后的值。如果 Promise 被 reject,则会抛出异常。

执行顺序

const getData = async () => {
    // 等待一个立即解决的 Promise (或者普通值,会被自动包装)
    let y = await "Hello World";
    console.log(y);
};

console.log(1);       // 1. 首先执行
getData();            // 2. 调用 async 函数,开始执行直到遇到 await
console.log(2);       // 3. 主线程继续执行,打印 2 (此时 getData 内部暂停)
                      // 4. 微任务队列中的 getData 恢复,打印 "Hello World"

输出顺序:

  1. 1

  2. 2

  3. Hello World

原理解析:

  • async 将函数转换为返回 Promise 的异步函数。

  • await 让函数“暂停”在此行,将后续代码放入微任务队列。

  • 主线程不受影响,继续执行 console.log(2)

  • 当 Promise 结算后,函数恢复执行并打印结果。


错误处理

在 Promise 链式调用中,我们使用 .catch() 处理错误。而在 async/await 中,推荐使用标准的 try...catch 语句,这使得错误处理逻辑更加直观和集中。

机制说明

  • Resolve: 当异步任务成功完成时,await 返回结果值。

  • Reject: 当异步任务失败时,await 会抛出一个异常,该异常可以被外层的 try...catch 捕获。

实战示例

网络请求处理

async function fetchData() {
  try {
    // 1. 发送 HTTP 请求并等待响应
    // 如果网络断开或 URL 错误,fetch 会 reject,此处直接跳转到 catch
    let response = await fetch('https://api.example.com/data');
    
    // 2. 检查 HTTP 状态码 (fetch 只有网络错误才会 reject,404/500 需手动处理)
    if (!response.ok) {
        throw new Error(`HTTP 错误!状态码:${response.status}`);
    }

    // 3. 解析 JSON 数据并等待解析完成
    let data = await response.json();
    
    // 4. 成功获取数据
    console.log("获取到的数据:", data);
    
  } catch (error) {
    // 5. 统一捕获所有错误(网络错误、JSON 解析错误、手动抛出的错误)
    console.error('获取数据失败:', error.message);
  }
}

fetchData();

重点解析

  • await fetch(...): 发起请求。若发生网络故障,Promise 被 reject,控制权立即转移到 catch 块。

  • await response.json(): 解析 body。若返回内容不是合法 JSON,会抛出异常,同样被 catch 捕获。

  • try...catch: 包裹整个异步流程,任何一步出错都能被统一处理,避免了在每个 .then() 后都写 .catch() 的冗余。