xhr、fetch和axios

XMLHttpRequest (XHR)

XMLHttpRequest 是最早用于在瀏覽器中進行異步網絡請求的 API。它允許網頁在不刷新整個頁面的情況下與服務器交換數據。

// 創建 XHR 對象
const xhr = new XMLHttpRequest();// 初始化請求
xhr.open('GET', 'https://api.example.com/data', true);// 設置請求頭(可選)
xhr.setRequestHeader('Content-Type', 'application/json');// 設置響應類型(可選)
xhr.responseType = 'json';// 監聽狀態變化
xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status === 200) {// 請求成功console.log('Response:', xhr.response);} else {// 請求失敗console.error('Error:', xhr.status);}}
};// 可選:處理錯誤
xhr.onerror = function() {console.error('Network error occurred');
};// 發送請求
xhr.send();
  • 優點:廣泛兼容、支持進度事件、功能全面
  • 缺點:API 復雜、回調嵌套問題(回調地獄)、不支持 Promise

回調地獄問題

// 第一個請求
const xhr1 = new XMLHttpRequest();
xhr1.open('GET', '/api/data1', true);
xhr1.onreadystatechange = function() {if (xhr1.readyState === 4 && xhr1.status === 200) {const data1 = JSON.parse(xhr1.responseText);// 第二個請求(依賴第一個請求的結果)const xhr2 = new XMLHttpRequest();xhr2.open('GET', `/api/data2?param=${data1.id}`, true);xhr2.onreadystatechange = function() {if (xhr2.readyState === 4 && xhr2.status === 200) {const data2 = JSON.parse(xhr2.responseText);// 第三個請求(依賴第二個請求的結果)const xhr3 = new XMLHttpRequest();xhr3.open('POST', '/api/submit', true);xhr3.setRequestHeader('Content-Type', 'application/json');xhr3.onreadystatechange = function() {if (xhr3.readyState === 4 && xhr3.status === 200) {const result = JSON.parse(xhr3.responseText);console.log('最終結果:', result);}};xhr3.send(JSON.stringify({ data1, data2 }));}};xhr2.send();}
};
xhr1.send();

為什么不能按順序寫 XHR 請求?

在異步編程中,代碼的書寫順序執行順序是兩回事。當你按順序寫 XHR 請求時,它們會立即開始執行,但不會等待前一個請求完成。這會導致嚴重問題:

// 錯誤示例:按順序寫但不嵌套
const xhr1 = new XMLHttpRequest();
xhr1.open('GET', '/api/data1', true);
xhr1.onreadystatechange = function() { /* 處理 data1 */ };
xhr1.send();const xhr2 = new XMLHttpRequest();
xhr2.open('GET', '/api/data2?param=???', true); // 這里需要 data1.id,但 data1 可能還沒返回
xhr2.onreadystatechange = function() { /* 處理 data2 */ };
xhr2.send();const xhr3 = new XMLHttpRequest();
xhr3.open('POST', '/api/submit', true);
xhr3.setRequestHeader('Content-Type', 'application/json');
xhr3.onreadystatechange = function() { /* 處理結果 */ };
xhr3.send(JSON.stringify({ data1, data2 })); // data1 和 data2 都可能不存在

為什么會出錯?

  1. 數據依賴問題

    • 第二個請求需要第一個請求的結果 (data1.id)

    • 第三個請求需要前兩個請求的結果 (data1?和?data2)

  2. 執行順序不確定

    • 異步請求的完成時間不可預測

    • 即使請求 1 先發送,請求 2 和 3 可能先完成

  3. 閉包問題

    • 回調函數中的變量會捕獲外部作用域

    • 如果不嵌套,變量可能在回調執行時已被修改

嵌套的核心目的:確保執行順序

通過嵌套回調,你實際上是在告訴程序:
"只有當請求 1 成功完成后,才開始請求 2;只有當請求 2 成功完成后,才開始請求 3"

解決方案

? ??

Fetch API

Fetch API 是現代瀏覽器提供的用于替代 XHR 的新標準,它基于 Promise,提供了更簡潔、更強大的接口來處理網絡請求。

主要特點:

  • 基于 Promise,避免回調地獄
  • 支持 async/await 語法
  • 提供 Request 和 Response 對象
  • 支持跨域請求和 CORS
  • 支持流式響應體處理

基本用法

// 簡單的 GET 請求
fetch('https://api.example.com/data').then(response => {if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}return response.json(); // 解析為 JSON}).then(data => {console.log('Response:', data);}).catch(error => {console.error('Fetch error:', error);});// 使用 async/await
async function fetchData() {try {const response = await fetch('https://api.example.com/data');if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}const data = await response.json();console.log('Response:', data);} catch (error) {console.error('Fetch error:', error);}
}// 帶參數的 POST 請求
fetch('https://api.example.com/submit', {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify({ name: 'John', age: 30 }),
}).then(response => response.json()).then(data => console.log(data)).catch(error => console.error('Error:', error));

優缺點:

  • 優點:現代 API、基于 Promise、更簡潔、支持流式處理
  • 缺點:不支持進度事件、錯誤處理需要額外檢查狀態碼、舊瀏覽器兼容性差(需要 polyfill)

Fetch API 的局限性詳解

1. 不支持進度事件

Fetch API 的主要設計目標是提供一個現代、簡潔的網絡請求接口,但它沒有內置的進度事件支持。在上傳或下載大文件時,這是一個明顯的不足:

XHR 的進度支持:XHR 通過?onprogress?事件提供上傳和下載進度:

xhr.upload.onprogress = function(event) {if (event.lengthComputable) {const percentComplete = (event.loaded / event.total) * 100;console.log(`上傳進度: ${percentComplete}%`);}
};

Fetch 的替代方案:Fetch 需要使用更復雜的?ReadableStream?API 來實現進度:

fetch('large-file.zip').then(response => {const reader = response.body.getReader();const contentLength = response.headers.get('Content-Length');let receivedLength = 0;return new Response(new ReadableStream({async start(controller) {while (true) {const { done, value } = await reader.read();if (done) break;receivedLength += value.length;const percentComplete = (receivedLength / contentLength) * 100;console.log(`下載進度: ${percentComplete}%`);controller.enqueue(value);}controller.close();}}));});
2. 錯誤處理需要額外檢查狀態碼

Fetch API 的設計與傳統 HTTP 錯誤處理模式不同:

  • Fetch 的錯誤處理機制
    • 只有網絡錯誤(如斷網、DNS 失敗)才會觸發?reject
    • HTTP 錯誤(如 404、500)不會觸發?reject,而是返回一個狀態碼非 2xx 的?Response?對象
fetch('https://example.com/non-existent').then(response => {// 這里會執行,即使服務器返回 404if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}return response.json();}).catch(error => {console.error('Fetch error:', error);});

XHR 的錯誤處理:XHR 的?onerror?事件會捕獲網絡錯誤,而 HTTP 錯誤通過狀態碼判斷:

xhr.onerror = function() {console.error('網絡錯誤');
};xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status >= 400) {console.error('HTTP 錯誤:', xhr.status);}}
};
3. 舊瀏覽器兼容性差(需要 polyfill)

AXIOS和fetch差別

Axios 是一個基于 Promise 的 HTTP 客戶端,專為瀏覽器和 Node.js 設計。它與原生 Fetch API 有許多區別,這些區別影響著開發者的選擇。

具體差異詳解
1 錯誤處理

Axios:HTTP 錯誤(404、500 等)會直接 reject Promise

axios.get('/api/data').then(response => {// 僅在 HTTP 狀態碼為 2xx 時執行console.log(response.data);}).catch(error => {// 處理所有錯誤(網絡錯誤和 HTTP 錯誤)console.error('請求失敗:', error.response?.status);});

Fetch:HTTP 錯誤不會 reject,需要手動檢查狀態碼

在 Fetch 請求的?then?回調中,你需要檢查?response.ok?屬性或?response.status?狀態碼:

fetch('https://api.example.com/data').then(response => {// 檢查響應狀態if (!response.ok) {// 手動拋出錯誤,使 Promise 被 rejectthrow new Error(`HTTP error! status: ${response.status}`);}// 請求成功,繼續處理響應return response.json();}).then(data => console.log('數據:', data)).catch(error => console.error('請求失敗:', error));

2 數據格式處理

Axios:自動解析 JSON 響應

axios.get('/api/data').then(response => {// response.data 已經是解析后的 JSON 對象console.log(response.data.name);});

Fetch:需要手動解析響應

fetch('/api/data').then(response => response.json()) // 手動解析為 JSON.then(data => console.log(data.name));

3 請求取消

Axios:使用?CancelToken

const source = axios.CancelToken.source();axios.get('/api/data', {cancelToken: source.token
})
.then(response => console.log(response))
.catch(thrown => {if (axios.isCancel(thrown)) {console.log('請求被取消:', thrown.message);}
});// 取消請求
source.cancel('用戶取消了請求');

Fetch:使用?AbortController(需要現代瀏覽器支持)

const controller = new AbortController();
const signal = controller.signal;fetch('/api/data', { signal }).then(response => response.json()).then(data => console.log(data)).catch(error => {if (error.name === 'AbortError') {console.log('請求被取消');}});// 取消請求
controller.abort();

4 進度事件

Axios:內置支持上傳和下載進度

// 下載進度
axios.get('/large-file', {onDownloadProgress: progressEvent => {const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);console.log(`下載進度: ${percentCompleted}%`);}
});// 上傳進度
axios.post('/upload', formData, {onUploadProgress: progressEvent => {const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);console.log(`上傳進度: ${percentCompleted}%`);}
});

Fetch:需要使用 ReadableStream 手動實現

fetch('/large-file').then(response => {const contentLength = response.headers.get('Content-Length');const reader = response.body.getReader();let receivedLength = 0;return new Response(new ReadableStream({async start(controller) {while (true) {const { done, value } = await reader.read();if (done) break;receivedLength += value.length;const percentComplete = (receivedLength / contentLength) * 100;console.log(`下載進度: ${percentComplete}%`);controller.enqueue(value);}controller.close();}}));});
5 攔截器

Axios:支持請求和響應攔截器,便于統一處理

// 添加請求攔截器
axios.interceptors.request.use(config => {// 在發送請求之前做些什么config.headers.Authorization = `Bearer ${token}`;return config;},error => {// 對請求錯誤做些什么return Promise.reject(error);}
);// 添加響應攔截器
axios.interceptors.response.use(response => {// 對響應數據做點什么return response;},error => {// 對響應錯誤做點什么if (error.response.status === 401) {// 處理未授權情況}return Promise.reject(error);}
);

Fetch:不直接支持攔截器,需要手動實現

function fetchWithInterceptor(url, options) {// 請求攔截const modifiedOptions = {...options,headers: {...options.headers,Authorization: `Bearer ${token}`}};return fetch(url, modifiedOptions).then(response => {// 響應攔截if (response.status === 401) {// 處理未授權情況}return response;});
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/81998.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/81998.shtml
英文地址,請注明出處:http://en.pswp.cn/web/81998.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

電腦驅動程序更新工具, 3DP Chip 中文綠色版,一鍵更新驅動!

介紹 3DP Chip 是一款免費的驅動程序更新工具,可以幫助用戶快速、方便地識別和更新計算機硬件驅動程序。 驅動程序更新工具下載 https://pan.quark.cn/s/98895d47f57c 軟件截圖 軟件特點 簡單易用:用戶界面簡潔明了,操作方便,…

機器學習與深度學習06-決策樹02

目錄 前文回顧5.決策樹中的熵和信息增益6.什么是基尼不純度7.決策樹與回歸問題8.隨機森林是什么 前文回顧 上一篇文章地址:鏈接 5.決策樹中的熵和信息增益 熵和信息增益是在決策樹中用于特征選擇的重要概念,它們幫助選擇最佳特征進行劃分。 熵&#…

【Kotlin】數字字符串數組集合

【Kotlin】簡介&變量&類&接口 【Kotlin】數字&字符串&數組&集合 文章目錄 Kotlin_數字&字符串&數組&集合數字字面常量顯式轉換數值類型轉換背后發生了什么 運算字符串字符串模板字符串判等修飾符數組集合通過序列提高效率惰性求值序列的操…

oscp練習PG Monster靶機復現

端口掃描 nmap -A -p- -T4 -Pn 192.168.134.180 PORT STATE SERVICE VERSION 80/tcp open http Apache httpd 2.4.41 ((Win64) OpenSSL/1.1.1c PHP/7.3.10) |_http-server-header: Apache/2.4.41 (Win64) OpenSSL/1.1.1c PHP/7.3.10 | http-methods:…

近期知識庫開發過程中遇到的一些問題

我們正在使用Rust開發一個知識庫系統,遇到了一些問題,在此記錄備忘。 錯誤:Unable to make method calls because underlying connection is closed 場景:在docker中調用headless_chrome時出錯 原因:為減小鏡像大小&am…

Ubuntu 22.04 系統下 Docker 安裝與配置全指南

Ubuntu 22.04 系統下 Docker 安裝與配置全指南 一、前言 Docker 作為現代開發中不可或缺的容器化工具,能極大提升應用部署和環境管理的效率。本文將詳細介紹在 Ubuntu 22.04 系統上安裝與配置 Docker 的完整流程,包括環境準備、安裝步驟、權限配置及鏡…

C#獲取磁盤容量:代碼實現與應用場景解析

C#獲取磁盤容量:代碼實現與應用場景解析 在軟件開發過程中,尤其是涉及文件存儲、數據備份等功能時,獲取磁盤容量信息是常見的需求。通過獲取磁盤的可用空間和總大小,程序可以更好地進行資源管理、預警提示等操作。在 C# 語言中&a…

2025年- H56-Lc164--200.島嶼數量(圖論,深搜)--Java版

1.題目描述 2.思路 (1)主函數,存儲圖結構 (2)主函數,visit數組表示已訪問過的元素 (3)輔助函數,用遞歸(深搜),遍歷以已訪問過的元素&…

詳細到用手撕transformer下半部分

之前我們討論了如何實現 Transformer 的核心多頭注意力機制,那么這期我們來完整地實現整個 Transformer 的編碼器和解碼器。 Transformer 架構最初由 Vaswani 等人在 2017 年的論文《Attention Is All You Need》中提出,專為序列到序列(seq2s…

WPF事件處理器+x名稱空間

目錄 ?編輯 一、事件處理器知識點 1. XAML中的事件綁定 2. C#中的事件處理方法 3. 方法簽名解釋 4. 命名規范 工作流程 二、導入引用名稱空間 三、x名稱空間及其常用元素 (1)x名稱空間的由來和作用 (2)x名稱空間里都有…

Axure設計案例——科技感漸變線性圖

想讓數據變化趨勢展示告別枯燥乏味,成為吸引觀眾目光的亮點嗎?快來看看這個Axure設計的科技感漸變線性圖案例!科技感設計風格憑借炫酷的漸變色彩打破傳統線性圖的單調,營造出一種令人過目難忘的視覺體驗。每一條線條都仿佛是流動的…

Git全流程操作指南

Git全流程操作指南 一、Git 環境配置 1. 安裝 Git Windows:下載 Git for Windows macOS:brew install git Linux: sudo apt-get update && sudo apt-get install git # Debian/Ubuntu sudo yum install git …

AI與軟件工程結合的未來三年發展路徑分析

基于對數字化、制造業、工業、零售業等行業的系統調研,以及微軟、谷歌、阿里、華為等大廠的實踐案例,我們可以預見未來三年AI與軟件工程結合將呈現以下發展路徑和趨勢。 一、技術應用維度 1. AI輔助編程工具全面普及 未來三年,AI輔助編程工…

tiktoken學習

1.tiktoken是OpenAI編寫的進行高效分詞操作的庫文件。 2.操作過程: enc tiktoken.get_encoding("gpt2") train_ids enc.encode_ordinary(train_data) val_ids enc.encode_ordinary(val_data) 以這段代碼為例,get_encoding是創建了一個En…

DeepSeek 賦能文化遺產數字化修復:AI 重構千年文明密碼

目錄 一、引言二、文化遺產數字化修復概述2.1 文化遺產數字化修復的意義2.2 傳統數字化修復方法與局限 三、DeepSeek 技術剖析3.1 DeepSeek 技術原理與核心優勢3.2 相比其他技術的獨特之處 四、DeepSeek 在文化遺產數字化修復中的應用4.1 破損文物的智能修復4.2 文化遺產的虛擬…

leetcode題解513:找樹左下角的值(遞歸中的回溯處理)!

一、題目內容: 題目要求找到一個二叉樹的最底層最左邊節點的值。具體來說,我們需要從根節點開始遍歷二叉 樹,找到最深的那層中的最左邊的節點,并返回該節點的值。因為要先找到最底層左側的值,所以我們選擇遍歷順序一定…

C#面試問題41-60

41. What is the Singleton design pattern? Singleton is a class that only allows creating a single instance of itselt. 單例設計模式是一個類,它只允許創建自己的單個實例。 構造函數防止他在單例類以外的地方被調用。 使用情景:need a sing…

筆記思考法

掌握麥肯錫流筆記術,對大家來說有以下幾種好處: 1) 可以將自己的思考可視化,使之變得更加清晰 2) 避免無用功 3) 經常能夠提出有創意的想法 4) 遇到問題時能夠及時找到解決辦法 5) 不管面對什么情況都能夠找出真正有效的解決辦法 為什么僅僅通過改變使用…

Rust 學習筆記:關于閉包的練習題

Rust 學習筆記:關于閉包的練習題 Rust 學習筆記:關于閉包的練習題問題 1問題 2以下程序能否通過編譯?若能,輸出是?以下程序能否通過編譯?若能,輸出是?考慮該 API,空白處填…

(一)微服務(垂直AP/分布式緩存/裝飾器Pattern)

文章目錄 項目地址一、創建第一個垂直API1.1 創建Common層1. ICommand接口2. IQuery接口 1.2 創建API1. 實體2. Handler3. endpoint 1.3 使用Marten作為ORM 二、Redis緩存2.1 使用緩存裝飾器1. 創建裝飾器2. 注冊裝飾器 2.2 創建docker-compose1. docker-compose2. docker-comp…