深入理解 Axios 攔截器與 Promise 異步機制:從源碼角度剖析異步編程的本質
本文將帶你從 Promise 基礎概念出發,深入理解 Axios 攔截器的內部實現原理,揭秘異步編程背後的核心機制。
📖 前言
在前端開發中,我們經常使用 Axios 進行 HTTP 請求,並通過攔截器來統一處理請求和響應。但你是否真正理解攔截器背後的工作原理?為什麼攔截器能夠按順序執行?為什麼有時候攔截器的 onRejected 不會被觸發?
本文將從 Promise 的基礎概念開始,逐步深入到 Axios 攔截器的內部實現,幫你徹底理解這套異步機制的本質。
1️⃣ Promise 基礎概念深入解析
1.1 Promise 三種狀態詳解
Promise 作為 JavaScript 中處理異步操作的核心機制,具有三種狀態:
| 狀態 | 描述 | 轉換條件 |
|---|---|---|
| pending | 初始狀態,既未成功也未失敗 | 無 |
| fulfilled | 成功態 | 調用 resolve(value) |
| rejected | 失敗態 | 調用 reject(reason) |
核心特性:
- 狀態一旦確定(fulfilled/rejected),就不可逆轉
- 狀態轉換隻能是:pending → fulfilled 或 pending → rejected
1.2 Promise 構造與執行機制
let p = new Promise((resolve, reject) => {
console.log('這裏立即同步執行'); // ✅ 構造函數立即執行
setTimeout(() => resolve('value'), 1000); // 異步操作
});
關鍵理解:
resolve(value)→ Promise 狀態變為 fulfilled,值為valuereject(reason)→ Promise 狀態變為 rejected,拒絕原因為reason- 構造函數內的代碼立即同步執行
- 只有
resolve/reject的調用會安排微任務到微任務隊列
1.3 then 方法的核心機制
p.then(onFulfilled, onRejected)
then 方法的本質:
- 返回一個新的 Promise(我們稱它為
p2) -
參數説明:
onFulfilled(value)→ 處理成功態,接收原 Promise resolve 的值onRejected(reason)→ 處理失敗態,接收原 Promise reject 的值
狀態決定規則:
- 回調返回普通值 → 新 Promise 成功 (p2),值為返回值(
undefined也算普通值) - 回調返回 Promise → 新 Promise (p2) 狀態跟隨返回的 Promise (p3)
- 回調拋出異常 → 新 Promise 失敗 (p2),拒絕原因是異常
重要細節:
- 如果只傳
onFulfilled,原 Promise rejected → 新 Promise 繼承原狀態 - 同一個 Promise 的狀態一旦確定,不會改變
1.4 catch 方法本質
p.catch(onRejected)
// 等價於:
p.then(undefined, onRejected)
- 捕獲鏈上前一個 Promise 的 reject
- 回調返回值的狀態規則與 then 相同
1.5 Promise 鏈中的狀態傳遞總結
| 鏈中節點 | 狀態來源 |
|---|---|
| 第一個 Promise | 構造函數中調用 resolve/reject |
| 第二個 Promise (then 返回) | 回調返回值或異常,若回調未傳且狀態不匹配,則繼承前一個狀態 |
| 第三個 Promise (catch 返回) | 回調返回值或異常,若返回 Promise,則新 Promise 狀態跟隨返回的 Promise |
2️⃣ Axios 攔截器機制深度剖析
2.1 請求攔截器詳解
axios.interceptors.request.use(
function onFulfilled(config) {
// 處理請求配置
return config;
},
function onRejected(error) {
// 處理攔截器鏈中的錯誤
return Promise.reject(error);
}
)
onFulfilled 參數詳解
- 參數
config:請求配置對象(JavaScript 對象) - 類型:
Object -
內容示例:
{ url: 'http://example.com', method: 'get', headers: { ... }, params: {}, data: null, timeout: 5000 }
可以修改的內容:
- headers(添加 token、修改 Content-Type)
- params(查詢參數)
- data(請求體數據)
- timeout(超時時間)
返回值處理:
- 普通對象 → 傳遞給下一環節(或發送請求)
- Promise → Axios 會等待 Promise resolve 再繼續
onRejected 觸發條件
- 只有當前一個攔截器的 onFulfilled 拋錯或返回
Promise.reject(...)才會觸發 - 處理攔截器鏈的異常情況
執行時機重點
- 請求攔截器的 onFulfilled 在請求發送前執行
- 並不是網絡請求成功後觸發
- 即使 Promise 狀態是 fulfilled,onFulfilled 也只處理 config,不依賴網絡結果
關鍵特性:單攔截器錯誤處理
axios.interceptors.request.use(
config => {
throw new Error('出錯了'); // 這裏拋錯
},
error => {
console.log('這裏不會執行'); // ❌ 不會觸發本攔截器的 onRejected
return Promise.reject(error);
}
);
原因:攔截器內部實現相當於 Promise.resolve(config).then(onFulfilled, onRejected),onFulfilled 拋錯產生的新 rejected Promise 不會回到當前 then 的 onRejected。
2.2 響應攔截器詳解
axios.interceptors.response.use(
function onFulfilled(response) {
// 處理成功響應
return response;
},
function onRejected(error) {
// 處理失敗響應
return Promise.reject(error);
}
)
onFulfilled 觸發條件
- 網絡請求成功後觸發
response.data是實際返回內容- 狀態碼 2xx 範圍內的響應
onRejected 觸發條件
// 以下情況會觸發響應攔截器的 onRejected:
// 1. HTTP 狀態碼 >= 400 (如 404, 500)
// 2. 網絡錯誤(超時、無網絡等)
// 3. 上一個攔截器拋出異常
// 4. 請求被取消
2.3 攔截器執行順序的關鍵差異
⚠️ 重要:請求攔截器和響應攔截器的執行順序不同!
// 註冊攔截器
axios.interceptors.request.use(config => {
console.log('請求攔截器 1');
return config;
});
axios.interceptors.request.use(config => {
console.log('請求攔截器 2');
return config;
});
axios.interceptors.response.use(response => {
console.log('響應攔截器 1');
return response;
});
axios.interceptors.response.use(response => {
console.log('響應攔截器 2');
return response;
});
// 執行順序:
// 請求攔截器 2 → 請求攔截器 1 → 發送請求 → 響應攔截器 1 → 響應攔截器 2
- 請求攔截器:倒序執行(後註冊的先執行)
- 響應攔截器:正序執行(先註冊的先執行)
3️⃣ Axios 內部實現原理深度解析
3.1 核心執行流程
讓我們深入 Axios 源碼,理解攔截器的實現原理:
// Axios 內部 request 方法(簡化版)
function request(config) {
// 1. 合併配置
config = mergeConfig(this.defaults, config);
// 2. 🎯 創建初始 Promise - 關鍵步驟!
let promise = Promise.resolve(config);
// 3. 應用請求攔截器(倒序)
this.interceptors.request.forEachReverse((interceptor) => {
promise = promise.then(
interceptor.fulfilled, // 用户傳入的第一個函數
interceptor.rejected // 用户傳入的第二個函數
);
});
// 4. 發送實際網絡請求
promise = promise.then(dispatchRequest, undefined);
// 5. 應用響應攔截器(正序)
this.interceptors.response.forEach((interceptor) => {
promise = promise.then(
interceptor.fulfilled,
interceptor.rejected
);
});
return promise; // 返回最終 Promise 給用户
}
3.2 Promise.resolve(config) 的關鍵作用
Q:這個 Promise 對象是誰創建的?
A:是 Axios 內部代碼創建的,不是用户創建的。
創建時機和作用
// 用户調用
axios.get('/api/users');
// Axios 內部立即執行:
let config = { url: '/api/users', method: 'get', /* 默認配置... */ };
let promise = Promise.resolve(config); // 🎯 在這裏創建!
// 此時 promise 的狀態:
// - state: fulfilled
// - value: config 對象
為什麼要用 Promise.resolve(config)?
// Promise.resolve() 的作用:創建一個立即 fulfilled 的 Promise
Promise.resolve('hello') // 立即創建 fulfilled 狀態的 Promise
// 等價於:
new Promise((resolve) => {
resolve('hello');
});
// 在 Axios 中的作用:
let promise = Promise.resolve(config); // 立即 fulfilled,值是 config
// 這樣第一個攔截器就能立即接收到 config:
promise.then(function(config) {
console.log('收到配置:', config); // config 就是上面的配置對象
return config;
});
3.3 完整執行示例
// 1. 用户調用
axios.get('/api/users');
// 2. Axios 內部執行流程:
function request(config) {
// Step 1: 準備配置
config = { url: '/api/users', method: 'get', headers: {}, /*...*/ };
// Step 2: 創建起始 Promise(fulfilled 狀態,值為 config)
let promise = Promise.resolve(config);
// Step 3: 應用請求攔截器鏈
// promise = promise.then(interceptor2.fulfilled, interceptor2.rejected);
// promise = promise.then(interceptor1.fulfilled, interceptor1.rejected);
// Step 4: 發送網絡請求
// promise = promise.then(dispatchRequest); // 返回包含 response 的 Promise
// Step 5: 應用響應攔截器鏈
// promise = promise.then(responseInterceptor1.fulfilled, responseInterceptor1.rejected);
// promise = promise.then(responseInterceptor2.fulfilled, responseInterceptor2.rejected);
// Step 6: 返回最終 Promise 給用户
return promise;
}
3.4 攔截器存儲結構
// Axios 內部存儲攔截器的結構(簡化)
class InterceptorManager {
constructor() {
this.handlers = []; // 存儲攔截器數組
}
use(fulfilled, rejected) {
this.handlers.push({
fulfilled, // 用户傳入的第一個函數
rejected // 用户傳入的第二個函數
});
return this.handlers.length - 1; // 返回索引,用於移除攔截器
}
forEach(fn) {
this.handlers.forEach(handler => {
if (handler !== null) {
fn(handler);
}
});
}
}
// 使用示例:
const requestInterceptors = new InterceptorManager();
requestInterceptors.use(
function(config) { return config; }, // 存儲為 handler.fulfilled
function(error) { return Promise.reject(error); } // 存儲為 handler.rejected
);
4️⃣ 核心知識點總結
4.1 Promise 核心機制
-
Promise 是狀態機
- pending → fulfilled / rejected
- 狀態不可逆
- then/catch 返回新的 Promise
-
then 參數機制
- onFulfilled:前一個 Promise fulfilled 時執行
- onRejected:前一個 Promise rejected 時執行
- 回調返回值決定新 Promise 狀態
-
catch 是 then 的語法糖
p.catch(fn)=p.then(undefined, fn)
4.2 Axios 攔截器核心機制
-
攔截器鏈本質是 Promise 鏈
- 請求攔截器 onFulfilled:處理 config → 在請求發送前執行
- 請求攔截器 onRejected:處理攔截器鏈中前一個 reject
- 響應攔截器 onFulfilled:處理網絡請求成功的 response
- 響應攔截器 onRejected:處理網絡請求失敗或上一個攔截器異常
-
執行順序差異
- 請求攔截器:倒序執行(後註冊先執行)
- 響應攔截器:正序執行(先註冊先執行)
-
攔截器返回值規則
- 返回普通值 → 繼續向下傳遞
- 返回 Promise → 等待 Promise resolve/reject
- 拋出異常 → 下一個 onRejected 捕獲
4.3 單攔截器 vs 多攔截器差異
// 單攔截器:onFulfilled 拋錯不會觸發本攔截器的 onRejected
axios.interceptors.request.use(
config => { throw new Error('錯誤'); }, // 拋錯
error => { console.log('不執行'); } // 不會執行
);
// 多攔截器:前一個攔截器 reject → 下一個攔截器 onRejected 執行
axios.interceptors.request.use(
config => { throw new Error('錯誤'); }, // 第一個攔截器拋錯
error => { console.log('不執行'); } // 不會執行
);
axios.interceptors.request.use(
config => { return config; },
error => { console.log('會執行'); } // 會捕獲上一個攔截器的錯誤
);
5️⃣ 實戰應用場景
5.1 統一添加認證 Token
axios.interceptors.request.use(
config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => Promise.reject(error)
);
5.2 統一錯誤處理
axios.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
// 跳轉到登錄頁
window.location.href = '/login';
}
return Promise.reject(error);
}
);
5.3 請求和響應日誌記錄
// 請求日誌
axios.interceptors.request.use(config => {
console.log(`發送請求: ${config.method?.toUpperCase()} ${config.url}`);
return config;
});
// 響應日誌
axios.interceptors.response.use(
response => {
console.log(`響應成功: ${response.status} ${response.config.url}`);
return response;
},
error => {
console.error(`請求失敗: ${error.message}`);
return Promise.reject(error);
}
);
6️⃣ 總結
通過本文的深入分析,我們瞭解到:
- Axios 攔截器的本質是 Promise 鏈,每個攔截器都是鏈上的一個節點
- Promise.resolve(config) 是整個鏈的起點,由 Axios 內部創建
- 攔截器的執行順序有差異:請求攔截器倒序,響應攔截器正序
- 單攔截器內部的錯誤處理機制:onFulfilled 拋錯不會觸發本攔截器的 onRejected
- 理解 Promise 狀態傳遞機制是掌握攔截器的關鍵
這些概念不僅適用於 Axios,對於理解其他基於 Promise 的異步庫(如 fetch API 的封裝庫)也有重要意義。掌握了這些原理,你就能更好地設計和調試複雜的異步處理邏輯。
💡 延伸閲讀建議:
- Promise/A+ 規範
- Axios 源碼分析
- 微任務與宏任務機制
- async/await 與 Promise 的關係
如果這篇文章對你有幫助,請點贊支持!有問題歡迎在評論區討論。 🚀