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
,你就掌握了與世界各地服務器對話的能力。在接下來的文章中,我們將學習如何組織我們的代碼,使其更具可維護性和可重用性——歡迎來到模塊化的世界!