深入理解 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 的关系
如果这篇文章对你有帮助,请点赞支持!有问题欢迎在评论区讨论。 🚀