JavaScript异步请求实例:Fetch API与async/await用法
深入讲解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关键字来分别等待响应头就绪与数据流解析完成。
二、基础实战:使用 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;
}
}
在上述代码中,整个流程分为四个核心执行步骤:
- 使用
async关键字修饰函数,声明该函数内部支持使用await,且该函数本身总是返回一个 Promise。 - 通过
await fetch(url)挂起当前函数执行,等待远程服务器的响应头返回。 - 通过
response.ok判断状态码是否落在 200~299 区间内,若不是则主动抛出错误。 - 通过
await response.json()再次挂起函数,等待数据体下载并反序列化为 JavaScript 对象。
三、进阶实战: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;
}
}
Content-Type,在跨域携带凭证(如 Cookie)时,还需在配置项中指定 credentials: 'include',否则浏览器默认不会向跨域地址发送任何鉴权凭证。
四、Promise 链式调用与 async/await 的结构对比
在 async/await 诞生前,开发者主要使用 fetch().then().then().catch() 这种 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业务状态码校验、响应体流式解析与最终业务处理。下图汇总了这一完整的判定决策链路:
在日常开发中,总结以下四项工程守则:
- 双重 await:记住
fetch()产生的是响应对象的 Promise,而response.json()产生的是解析结果的 Promise,缺一不可。 - 严格检查
response.ok:不可过度信任catch块对 HTTP 错误状态的捕获能力,必须对状态码进行主动校验。 - 配置
Content-Type:发起 POST 或 PUT 请求传递 JSON 载荷时,必须显式附加'Content-Type': 'application/json'请求头。 - 超时控制:Fetch 原生不支持
timeout配置,在生产环境中应搭配AbortController实现请求超时熔断,防止连接长期挂起占用客户端资源。
JS语法错误会阻止加载吗?区分SyntaxError与资源加载失败
2026-11-05 16:08:46
JavaScript常见SyntaxError语法错误排查与修复方法
2026-11-05 13:58:44
JS中slice、substring和substr截取字符串的区别
2026-11-05 13:43:47
JS 转 TS 手动迁移最佳实践:从配置 tsconfig 到消除 any 类型
2026-11-05 11:53:32
为什么JS数组push后得到的是数字而不是数组?
2026-11-05 09:59:18








