Langchain系列文章目录
01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来
Python系列文章目录
PyTorch系列文章目录
机器学习系列文章目录
深度学习系列文章目录
Java系列文章目录
JavaScript系列文章目录
01-【JavaScript-Day 1】从零开始:全面了解 JavaScript 是什么、为什么学以及它与 Java 的区别
02-【JavaScript-Day 2】开启 JS 之旅:从浏览器控制台到 <script>
标签的 Hello World 实践
03-【JavaScript-Day 3】掌握JS语法规则:语句、分号、注释与大小写敏感详解
04-【JavaScript-Day 4】var
完全指南:掌握变量声明、作用域及提升
05-【JavaScript-Day 5】告别 var
陷阱:深入理解 let
和 const
的妙用
06-【JavaScript-Day 6】从零到精通:JavaScript 原始类型 String, Number, Boolean, Null, Undefined, Symbol, BigInt 详解
07-【JavaScript-Day 7】全面解析 Number 与 String:JS 数据核心操作指南
08-【JavaScript-Day 8】告别混淆:一文彻底搞懂 JavaScript 的 Boolean、null 和 undefined
09-【JavaScript-Day 9】从基础到进阶:掌握 JavaScript 核心运算符之算术与赋值篇
10-【JavaScript-Day 10】掌握代码决策核心:详解比较、逻辑与三元运算符
11-【JavaScript-Day 11】避坑指南!深入理解JavaScript隐式和显式类型转换
12-【JavaScript-Day 12】掌握程序流程:深入解析 if…else 条件语句
13-【JavaScript-Day 13】告别冗长if-else:精通switch语句,让代码清爽高效!
14-【JavaScript-Day 14】玩转 for
循环:从基础语法到遍历数组实战
15-【JavaScript-Day 15】深入解析 while 与 do…while 循环:满足条件的重复执行
16-【JavaScript-Day 16】函数探秘:代码复用的基石——声明、表达式与调用详解
17-【JavaScript-Day 17】函数的核心出口:深入解析 return
语句的奥秘
18-【JavaScript-Day 18】揭秘变量的“隐形边界”:深入理解全局与函数作用域
19-【JavaScript-Day 19】深入理解 JavaScript 作用域:块级、词法及 Hoisting 机制
20-【JavaScript-Day 20】揭秘函数的“记忆”:深入浅出理解闭包(Closure)
21-【JavaScript-Day 21】闭包实战:从模块化到内存管理,高级技巧全解析
22-【JavaScript-Day 22】告别 function
关键字?ES6 箭头函数 (=>
) 深度解析
23-【JavaScript-Day 23】告别繁琐的参数处理:玩转 ES6 默认参数与剩余参数
24-【JavaScript-Day 24】从零到一,精通 JavaScript 对象:创建、访问与操作
25-【JavaScript-Day 25】深入探索:使用 for...in
循环遍历 JavaScript 对象属性
26-【JavaScript-Day 26】零基础掌握JavaScript数组:轻松理解创建、索引、长度和多维结构
27-【JavaScript-Day 27】玩转数组:push
, pop
, slice
, splice
等方法详解与实战
28-【JavaScript-Day 28】告别繁琐循环:forEach
, map
, filter
数组遍历三剑客详解
29-【JavaScript-Day 29】数组迭代进阶:掌握 reduce、find、some 等高阶遍历方法
30-【JavaScript-Day 30】ES6新特性:Set与Map,让你的数据管理更高效!
31-【JavaScript-Day 31】对象的“蓝图”详解:构造函数、new
与 instanceof
完全指南
32-【JavaScript-Day 32】深入理解 prototype、__proto__ 与原型链的奥秘
33-【JavaScript-Day 33】深入浅出 ES6 Class:从入门到精通面向对象新姿势
34-【JavaScript-Day 34】前后端数据交互的通用语:深入解析JSON
35-【JavaScript-Day 35】从 window 到 location,一文掌握浏览器对象模型 BOM
36-【JavaScript-Day 36】前端基石:深入理解 DOM 并精通五大元素选择器
37-【JavaScript-Day 37】在 DOM 树中“行走”:节点遍历
38-【JavaScript-Day 38】JS操控网页外观:从innerHTML到classList的全方位指南
39-【JavaScript-Day 39】从零到一,动态构建交互式网页的 DOM 节点操作秘籍
40-【JavaScript-Day 40】响应用户操作:事件监听与处理从入门到精通
41-【JavaScript-Day 41】JS 事件大全:click, keydown, submit, load 等常见事件详解与实战
42-【JavaScript-Day 42】深入解析事件冒泡与捕获:掌握事件委托的精髓
43-【JavaScript-Day 43】从单线程到事件循环:深入解析JS同步与异步核心机制
44-【JavaScript-Day 44】告别混乱:从回调函数到“回调地狱”的演进与解决方案
45-【JavaScript-Day 45】异步编程救星:深入解析 Promise 的状态、创建与链式调用
46-【JavaScript-Day 46】解锁 Promise 并发控制:深入解析 Promise.all、race 与 allSettled
47-【JavaScript-Day 47】告别 .then 链:用 async/await 写出诗一样的异步代码
48-【JavaScript-Day 48】告别 Ajax,拥抱现代网络请求:Fetch API 完全指南
文章目录
- Langchain系列文章目录
- Python系列文章目录
- PyTorch系列文章目录
- 机器学习系列文章目录
- 深度学习系列文章目录
- Java系列文章目录
- JavaScript系列文章目录
- 前言
- 一、Fetch API 概述
- 1.1 什么是 Fetch API?
- 1.2 Fetch API vs. XMLHttpRequest (XHR)
- 1.3 基本语法结构
- 二、发起 GET 请求
- 2.1 最简单的 GET 请求
- 2.2 解读 Response 对象
- 2.3 解析响应数据
- 2.4 结合 async/await 使用
- 三、发起 POST 请求
- 3.1 POST 请求的核心:`options` 对象
- 3.2 设置请求方法和头部
- 3.3 准备请求体 `body`
- 3.4 完整的 POST 请求示例
- 四、Fetch 中的错误处理
- 4.1 Fetch 的“奇怪”行为
- 4.2 如何正确处理 HTTP 错误
- 五、总结
前言
在上一篇文章中,我们学习了 async/await
,它为我们处理异步操作提供了终极的、如同同步代码般优雅的解决方案。现在,是时候将这项强大的技能应用到 Web 开发最核心的场景之一:与服务器进行数据交换了。过去,这项任务主要由 XMLHttpRequest
(XHR) 对象承担,但其设计复杂、基于事件的模式在现代 JavaScript 中显得有些格格不入。
今天,我们将隆重介绍现代 Web 的标准网络请求工具——Fetch API
。它是一个基于 Promise 的强大接口,能够让我们以更简洁、更直观、更强大的方式发送网络请求和处理响应。本文将带你从零开始,全面掌握 Fetch API
的使用,无论是获取数据(GET)还是提交数据(POST),你都能轻松应对。
一、Fetch API 概述
在深入代码之前,我们首先需要理解 Fetch API
是什么,以及它相较于传统方法的优势所在。
1.1 什么是 Fetch API?
Fetch API
提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的部分,例如请求和响应。它还提供了一个全局的 fetch()
方法,该方法提供了一种简单、合理的方式来跨网络异步获取资源。
这个 fetch()
方法被设计为现代、通用的网络请求工具,它天生基于 Promise
,这使得它能够完美地与我们之前学习的 async/await
语法结合,编写出既强大又易于阅读的异步代码。
其核心特性可以总结为:
- 现代性:作为
XMLHttpRequest
的替代者,旨在简化网络请求。 - Promise 驱动:
fetch()
返回一个 Promise 对象,让异步处理更加链式化和直观。 - 功能强大:提供了丰富的
Request
和Response
对象,可以对请求和响应进行精细化控制。 - 全局可用:
fetch()
方法存在于window
和worker
的全局作用域中,无需引入任何库即可使用。
1.2 Fetch API vs. XMLHttpRequest (XHR)
为了更好地理解 Fetch
的优势,我们可以将其与传统的 XMLHttpRequest
(XHR) 进行一个简单的对比。
特性 | XMLHttpRequest (XHR) | Fetch API |
---|---|---|
核心机制 | 基于事件和回调函数 | 基于 Promise |
语法 | 相对冗长和复杂,需要实例化对象并监听事件 | 语法简洁,一个函数调用即可发起请求 |
API 设计 | API 设计较为分散,配置和状态通过不同属性和方法管理 | API 设计更聚合,通过 Request 和 Response 对象进行统一管理 |
可读性 | 容易陷入“回调地狱” | 完美结合 Promise 链式调用和 async/await ,代码清晰 |
错误处理 | 通过 onerror 事件处理网络层错误,HTTP 状态码需在 onreadystatechange 中判断 | 仅在网络失败时 reject ,HTTP 错误状态(如 404, 500)需在 then 中手动检查 |
很明显,Fetch API
在语法和设计理念上都更符合现代 JavaScript 的开发模式。
1.3 基本语法结构
fetch()
方法的语法非常简单,它接受一个必需参数(资源路径)和一个可选参数(配置对象)。
fetch(resource, options).then(response => {// 处理响应对象 (Response)}).catch(error => {// 处理网络错误});
resource
: 你想要请求的资源 URL。这通常是一个字符串。options
(可选): 一个配置对象,允许你控制请求的各个方面,如请求方法 (method
)、请求头 (headers
)、请求体 (body
) 等。如果省略,默认为 GET 请求。
这个流程图揭示了一个关键点:fetch()
是一个两步过程。首先,它返回一个代表响应的 Response
对象;然后,你需要使用 Response
对象的方法来真正地解析响应体中的数据。
二、发起 GET 请求
GET 请求是最常见的网络请求类型,用于从服务器获取数据。使用 fetch
发起 GET 请求非常直接。
2.1 最简单的 GET 请求
当不提供 options
对象或 options.method
未指定时,fetch
默认执行 GET 请求。
// 示例:从 JSONPlaceholder 获取一篇博文
fetch('https://jsonplaceholder.typicode.com/posts/1').then(response => response.json()) // 第一步:解析响应体为 JSON.then(data => console.log(data)) // 第二步:获取并使用最终数据.catch(error => console.error('请求失败:', error));
在这个例子中:
- 我们向指定的 URL 发送了一个 GET 请求。
- 第一个
.then()
接收到一个Response
对象。我们调用它的.json()
方法,这个方法会读取响应体并尝试将其解析为 JSON。重要的是,.json()
方法本身也返回一个 Promise。 - 第二个
.then()
在.json()
的 Promise 完成后执行,接收到最终解析出的 JavaScript 对象,并将其打印到控制台。 .catch()
用于捕获在整个过程中可能发生的网络错误(例如,DNS 解析失败、用户离线)。
2.2 解读 Response 对象
在调用 .json()
或 .text()
之前,我们得到的 Response
对象本身包含了大量关于响应的有用信息。
属性 | 类型 | 描述 |
---|---|---|
ok | Boolean | 一个布尔值,表示请求是否成功。HTTP 状态码在 200-299 范围内为 true 。 |
status | Number | HTTP 响应的状态码,例如 200 表示成功,404 表示未找到。 |
statusText | String | 状态码对应的文本信息,例如 “OK” 或 “Not Found”。 |
headers | Headers | 一个 Headers 对象,允许你查询响应头信息。 |
url | String | 请求的完整 URL。 |
检查 response.ok
是处理请求的良好实践,我们将在错误处理部分详细讨论。
fetch('https://jsonplaceholder.typicode.com/posts/1').then(response => {console.log('请求成功!');console.log('状态码:', response.status); // 输出: 200console.log('状态文本:', response.statusText); // 输出: OKconsole.log('响应是否成功:', response.ok); // 输出: trueconsole.log('Content-Type 头:', response.headers.get('content-type')); // 输出: application/json; charset=utf-8return response.json();}).then(data => {console.log('获取到的数据:', data);});
2.3 解析响应数据
Response
对象提供了多种方法来处理不同格式的响应体,它们都返回 Promise。
response.json()
: 解析响应体为 JSON 对象。response.text()
: 解析响应体为纯文本。response.blob()
: 解析响应体为Blob
对象,用于处理图片、文件等二进制数据。response.formData()
: 解析响应体为FormData
对象。response.arrayBuffer()
: 解析响应体为ArrayBuffer
对象,用于处理通用的、固定长度的原始二进制数据。
2.4 结合 async/await 使用
使用 async/await
可以让 fetch
调用看起来更像同步代码,从而极大地提升了可读性。
// 使用 async/await 重写上面的 GET 请求
async function getPostData(postId) {try {// 等待 fetch 请求完成,获取 Response 对象const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${postId}`);// 检查响应是否成功if (!response.ok) {// 如果不成功,手动抛出错误throw new Error(`HTTP 错误! 状态: ${response.status}`);}// 等待响应体解析为 JSONconst data = await response.json();console.log(data);} catch (error) {// 捕获网络错误或我们手动抛出的错误console.error('获取数据失败:', error);}
}getPostData(1);
这段代码的功能与之前的 .then
链版本完全相同,但逻辑更清晰、更易于调试。
三、发起 POST 请求
POST 请求用于向服务器提交数据,通常用于创建新资源。这需要我们使用 fetch
的第二个参数——options
对象。
3.1 POST 请求的核心:options
对象
要发送 POST 请求,我们至少需要配置 method
, headers
, 和 body
这三个关键属性。
method
: HTTP 请求方法,设置为'POST'
。headers
: 一个包含请求头的对象。对于发送 JSON 数据的场景,必须设置'Content-Type': 'application/json'
,以告知服务器我们发送的是 JSON 格式的数据。body
: 请求的主体内容。注意:body
必须是一个字符串,因此我们需要使用JSON.stringify()
将 JavaScript 对象转换为 JSON 字符串。
3.2 设置请求方法和头部
const options = {method: 'POST', // 指定请求方法为 POSTheaders: {'Content-Type': 'application/json', // 告诉服务器,我们发送的是 JSON 数据// 'Authorization': 'Bearer YOUR_TOKEN' // 如果需要认证,可以在这里添加},// ... 后面会添加 body
};
3.3 准备请求体 body
假设我们要创建一个新的博文对象,首先定义这个对象,然后使用 JSON.stringify()
将其转换。
// 1. 定义要发送的 JavaScript 对象
const newPost = {title: '一篇关于 Fetch 的新文章',body: 'Fetch API 非常强大和灵活。',userId: 1,
};// 2. 将对象转换为 JSON 字符串
const requestBody = JSON.stringify(newPost);
3.4 完整的 POST 请求示例
现在,我们将所有部分组合起来,构建一个完整的 POST 请求。
async function createPost() {const newPostData = {title: '学习 Fetch API',body: '使用 async/await 发送 POST 请求。',userId: 10,};try {const response = await fetch('https://jsonplaceholder.typicode.com/posts', {method: 'POST', // 请求方法headers: {'Content-Type': 'application/json', // 请求头},body: JSON.stringify(newPostData), // 请求体});if (!response.ok) {throw new Error(`HTTP 错误! 状态: ${response.status}`);}// 服务器通常会返回新创建的资源(带有一个 id)const createdPost = await response.json();console.log('成功创建资源:', createdPost);// 预期的输出可能像这样: { id: 101, title: '...', body: '...', userId: 10 }} catch (error) {console.error('创建帖子失败:', error);}
}createPost();
这个例子清晰地展示了如何配置并发送一个 POST 请求,并处理服务器的响应。
四、Fetch 中的错误处理
错误处理是健壮应用程序的关键部分,而 fetch
的错误处理机制有一个非常重要的特点需要我们特别注意。
4.1 Fetch 的“奇怪”行为
一个常见的误区是:认为所有非 2xx 的 HTTP 状态(如 404 Not Found, 500 Internal Server Error)都会导致 fetch
的 Promise被 reject
。
事实并非如此。
fetch()
返回的 Promise 仅在遇到网络级别故障时(例如,无法连接服务器、CORS 策略阻止了请求等)才会被 reject
。对于服务器返回的任何 HTTP 状态码(包括 4xx 和 5xx),fetch
的 Promise 都会被 fulfill
(即成功解决),并返回一个 Response
对象。
这意味着,以下代码 无法 捕获到 404 错误:
// 错误示范:这样无法捕获到 404 错误
fetch('https://jsonplaceholder.typicode.com/posts/non-existent-url').then(response => response.json()) // 即使是 404,这里也会执行.then(data => console.log(data)) // 这里可能会因为 response.json() 失败而报错.catch(error => {// 这个 catch 块不会因为 404 而被触发console.error('这个 catch 不会捕获到 404 错误', error);});
4.2 如何正确处理 HTTP 错误
正确的做法是利用我们之前介绍的 response.ok
属性。这个属性为我们提供了一个简单的方式来判断 HTTP 状态码是否在成功范围内(200-299)。
正确的错误处理模式如下:
- 发起
fetch
请求。 - 在第一个
.then()
或await
之后,检查response.ok
的值。 - 如果
response.ok
是false
,则手动创建一个Error
对象并throw
它。 - 这样,这个被抛出的错误就可以被后续的
.catch()
块或try...catch
语句捕获。
// 正确的错误处理模式
async function getPostDataSafely(postId) {try {const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${postId}`);// 关键步骤:检查响应是否成功if (!response.ok) {// 如果不成功,构建一个包含状态码和文本的错误信息const errorText = await response.text(); // 尝试获取错误响应体throw new Error(`请求失败,状态码: ${response.status}. 响应: ${errorText}`);}const data = await response.json();console.log('获取数据成功:', data);} catch (error) {// 现在可以捕获所有类型的错误:网络错误和我们手动抛出的 HTTP 错误console.error('操作失败:', error.message);}
}// 测试一个存在的资源
getPostDataSafely(1);// 测试一个不存在的资源
getPostDataSafely(9999); // 将会触发 catch 块
通过这种模式,我们就能统一处理网络错误和应用层(HTTP)错误,让我们的代码更加健壮和可预测。
五、总结
Fetch API
是现代 JavaScript 中进行网络通信的基石。通过本文的学习,我们掌握了其核心用法和关键概念,现在让我们来回顾一下重点:
- 核心理念:
Fetch API
是一个基于Promise
的现代网络请求接口,通过全局fetch()
函数使用,它比传统的XMLHttpRequest
更简洁、更强大。 - 两步处理:
fetch()
的调用是一个两阶段的过程。第一阶段是获取Response
对象,它包含了响应的元数据(如状态码、头部)。第二阶段是调用Response
对象的方法(如.json()
,.text()
)来异步解析响应体,获取真实数据。 - 请求配置:对于 GET 以外的请求(如 POST),我们需要使用
fetch()
的第二个参数options
对象来详细配置请求,包括method
、headers
和body
。特别注意body
必须是字符串,通常使用JSON.stringify()
进行转换。 - 关键的错误处理:
fetch
的 Promise 只在网络失败时reject
。对于服务器返回的 HTTP 错误状态(如 404, 500),Promise 依然会fulfill
。因此,必须在代码中显式检查response.ok
属性,并手动抛出错误,才能被.catch()
或try...catch
统一捕获。 - 最佳实践:结合
async/await
使用fetch
可以让异步网络请求代码的结构变得扁平、清晰,是当前处理网络请求的最佳实践。
掌握了 Fetch API
,你就掌握了与世界各地服务器对话的能力。在接下来的文章中,我们将学习如何组织我们的代码,使其更具可维护性和可重用性——欢迎来到模块化的世界!