JavaScript异步请求实例:Fetch API与async/await用法

作者:小括号

时间:2026-10-30

浏览:84

深入讲解JavaScript中使用Fetch API结合async/await进行网络异步请求的完整实践,包含GET与POST实例、两阶段Promise原理解析、HTTP状态码检验与异常捕获。

文章目录

在现代Web开发中,通过JavaScript发起网络请求获取后端数据是构建动态页面的基础操作。传统的XMLHttpRequest配合回调函数的写法结构繁复且容易出现回调地狱,而Fetch API与async/await语法的结合,让异步网络通信具备了接近同步代码的线性直观结构。本文将通过具体的GET与POST实战用例,拆解Fetch API的两阶段Promise工作机制,并系统梳理在实际项目中必须处理的状态码验证与异常捕获策略。

一、Fetch API 与 async/await 的核心运作原理

Fetch API是现代浏览器原生的全局接口,用于替代旧式的Ajax机制。当调用fetch(url)时,它返回的是一个Promise对象。但与许多初学者的直观设想不同,这个Promise并不会直接交付最终的数据体(如JSON数据),而是分为两个明确的处理阶段:

  • 第一阶段(连接与响应头接收):当服务端返回HTTP响应头时,第一个Promise即被resolve,返回一个Response对象。此时底层TCP连接已建立,但响应报文的主体数据流(body)尚未完全下载完毕。
  • 第二阶段(数据流解析):通过调用Response对象提供的解析方法(如response.json()或response.text()),浏览器开始异步读取并解析完整的流式数据,这会触发第二个Promise。

正是由于这种“双阶段Promise”机制,在使用async/await处理Fetch请求时,通常需要先后使用两次await关键字来分别等待响应头就绪与数据流解析完成。

JavaScript异步请求实例:Fetch API与async/await用法
图1:Fetch API 的双阶段 Promise 响应处理机制流程

二、基础实战:使用 async/await 发起 GET 请求

GET请求是客户端从服务端检索资源的标准方法。在ES8(ECMAScript 2017)引入async/await之后,我们可以将异步代码以自上而下的同步方式编写,省去繁复的回调链。

下面是一个标准的GET请求函数实现,它展示了从定义异步函数到获取JSON数据的完整步骤:

// 实例:查询指定ID的用户详情
async function fetchUserProfile(userId) {
  const url = `https://api.example.com/users/${userId}`;

  try {
    // 步骤1:发起网络请求,等待响应头返回
    const response = await fetch(url, {
      method: 'GET',
      headers: {
        'Accept': 'application/json'
      }
    });

    // 步骤2:检查HTTP响应状态码
    if (!response.ok) {
      throw new Error(`HTTP请求失败,状态码: ${response.status} ${response.statusText}`);
    }

    // 步骤3:解析并等待响应体JSON数据
    const userData = await response.json();

    // 步骤4:使用获取到的数据
    console.log('获取到的用户数据:', userData);
    return userData;
  } catch (error) {
    // 步骤5:集中处理网络断开或业务异常
    console.error('fetchUserProfile 执行出错:', error.message);
    throw error;
  }
}

在上述代码中,整个流程分为四个核心执行步骤:

  1. 使用 async 关键字修饰函数,声明该函数内部支持使用 await,且该函数本身总是返回一个 Promise。
  2. 通过 await fetch(url) 挂起当前函数执行,等待远程服务器的响应头返回。
  3. 通过 response.ok 判断状态码是否落在 200~299 区间内,若不是则主动抛出错误。
  4. 通过 await response.json() 再次挂起函数,等待数据体下载并反序列化为 JavaScript 对象。
JavaScript异步请求实例:Fetch API与async/await用法
图2:使用 async/await 发起 GET 请求的四阶段执行时序

三、进阶实战:POST 请求与载荷配置

在向服务端提交表单、保存数据或触发计算时,必须使用POST等携带报文主体的请求。Fetch API通过第二个可选参数配置对象(init)来定制请求行为:

  • method: 明确指定为 'POST'。
  • headers: 必须显式指定 'Content-Type': 'application/json',通知服务器如何解析请求体。
  • body: 将JavaScript对象通过 JSON.stringify() 序列化为JSON格式的字符串。若直接传入对象,将被默认转为 [object Object] 导致服务端解析失败。
// 实例:提交新文章数据
async function createPost(postData) {
  const url = 'https://api.example.com/posts';

  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer YOUR_TOKEN_HERE'
      },
      body: JSON.stringify(postData) // 必须进行JSON序列化
    });

    if (!response.ok) {
      // 提取服务端返回的错误明细
      const errorDetail = await response.text();
      throw new Error(`创建文章失败 [${response.status}]: ${errorDetail}`);
    }

    const result = await response.json();
    return result;
  } catch (error) {
    console.error('提交请求失败:', error.message);
    throw error;
  }
}
参数提示:在配置 POST 请求时,除了常见的 Content-Type,在跨域携带凭证(如 Cookie)时,还需在配置项中指定 credentials: 'include',否则浏览器默认不会向跨域地址发送任何鉴权凭证。

四、Promise 链式调用与 async/await 的结构对比

在 async/await 诞生前,开发者主要使用 fetch().then().then().catch() 这种 Promise 链式写法。尽管链式写法已经解决了传统多层回调嵌套的问题,但在代码可读性、变量共享以及异常断点排查方面,async/await 展现了显著的工程优势。

JavaScript异步请求实例:Fetch API与async/await用法
图3:传统 Promise 链式写法与 async/await 语法的结构对比

对比两者的关键差异:

  • 作用域共享:在链式调用中,第一个 .then 中得到的 res 变量无法直接在后续的 .then 闭包中访问,除非定义外层全局变量;而 async/await 处于同一函数作用域,所有中间变量随时可用。
  • 异常捕获统一性:async/await 完美支持语言原生的 try...catch 语句,网络异常与本地语法错误可统一捕获,调试时可直接在 await 行设置断点单步调试。

五、关键防坑指南:HTTP 状态码校验与错误捕获

在使用 Fetch API 时,最容易造成隐蔽 Bug 的特性在于:Fetch API 在接收到 HTTP 404(资源不存在)或 500(服务器内部错误)时,Promise 不会被 reject。

只有当发生真正的底层网络故障(如网线拔出、域名无法解析、CORS跨域被安全策略拦截)时,Fetch 返回的 Promise 才会变为 rejected 状态并跳入 catch 块。这意味着,如果服务端返回了 404 Not Found 的 HTML 错误页面,直接执行 await response.json() 会由于解析非 JSON 字符串而抛出语法解析错误,掩盖了真实的状态码问题。

因此,严谨的业务请求必须显式检验 response.ok:

// 状态码防坑判断模式
const response = await fetch('/api/user/info');

// 关键检验点:response.ok 为 true 代表状态码位于 200 - 299
if (!response.ok) {
  // 根据不同的状态码执行差异化业务逻辑
  switch (response.status) {
    case 401:
      console.warn('登录凭据过期,重定向至登录页');
      break;
    case 403:
      console.warn('当前用户权限不足');
      break;
    case 404:
      console.warn('请求的目标资源不存在');
      break;
    case 500:
      console.error('服务器内部发生未捕获异常');
      break;
    default:
      console.error(`未知状态码响应: ${response.status}`);
  }
  throw new Error(`请求异常: ${response.status}`);
}

const data = await response.json();

六、全流程与开发规范总结

将 Fetch API 与 async/await 结合使用时,完整的请求生命周期应涵盖:网络请求发起、网络连通性校验、HTTP业务状态码校验、响应体流式解析与最终业务处理。下图汇总了这一完整的判定决策链路:

JavaScript异步请求实例:Fetch API与async/await用法
图4:Fetch API 与 async/await 异步请求全生命周期决策流

在日常开发中,总结以下四项工程守则:

  • 双重 await:记住 fetch() 产生的是响应对象的 Promise,而 response.json() 产生的是解析结果的 Promise,缺一不可。
  • 严格检查 response.ok:不可过度信任 catch 块对 HTTP 错误状态的捕获能力,必须对状态码进行主动校验。
  • 配置 Content-Type:发起 POST 或 PUT 请求传递 JSON 载荷时,必须显式附加 'Content-Type': 'application/json' 请求头。
  • 超时控制:Fetch 原生不支持 timeout 配置,在生产环境中应搭配 AbortController 实现请求超时熔断,防止连接长期挂起占用客户端资源。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章