axios * 工作方式及原理源码解析
作者:锋利的绵羊 发布时间:2023-07-02 16:38:36
axios * 的配置方式
本文所用 axios 版本号为:1.3.2
。
axios 中有两种 * :
axios.interceptors.request.use(onFulfilled, onRejected, options)
:配置请求 * 。synchronous:控制请求 * 是否为异步执行,默认为 true,每个 * 都可以单独设置,只要有一个设置为 false 则为同步执行,否则为异步执行。
runWhen:一个方法,接收 config 对象作为参数,在每次调用设定的 * 方法前调用,返回结果为 true 时执行 * 方法。
onFulfilled 方法在发送请求前执行,接收 config 对象,返回一个新的 config 对象,可在此方法内修改 config 对象。
onRejected 方法在 onFulfilled 执行错误后执行,接收 onFulfilled 执行后的错误对象。
options 配置参数
axios.interceptors.response.use(onFulfilled, onRejected, options)
:配置响应 * 。onFulfilled 方法在返回响应结果前调用,接收响应对象,返回一个新的响应对象,可在此方法内修改响应对象。
onRejected 与 options 同
axios.interceptors.request.use
。
axios.interceptors.request.use(
function (config) {
return config;
},
function (error) {
return Promise.reject(error);
}
);
axios.interceptors.response.use(
function (response) {
return response;
},
function (error) {
return Promise.reject(error);
}
);
可以添加多个 * :
axios.interceptors.request.use(
function (config) {
throw new Error("999");
return config;
},
function (error) {
console.log(1, error);
return Promise.reject(error);
}
);
axios.interceptors.request.use(
function (config) {
throw new Error("888");
return config;
},
function (error) {
console.log(2, error);
return Promise.reject(error);
}
);
axios.interceptors.response.use(
function (response) {
return response;
},
function (error) {
console.log(3, error);
return Promise.reject(error);
}
);
axios.interceptors.response.use(
function (response) {
return response;
},
function (error) {
console.log(4, error);
return Promise.reject(error);
}
);
axios.get("https://www.baidwwu.com").catch((error) => {
console.log(4, error);
});
// 2 Error: 888
// 1 Error: 888
// 3 Error: 888
// 4 Error: 888
// 5 Error: 888
先执行请求 * ,后执行响应 * 。
设置多个请求 * 的情况:后添加的 * 先执行。
设置多个响应 * 的情况:按照设置顺序执行。
use() 方法的定义
先来看 use() 方法相关代码:
this.interceptors = {
request: new InterceptorManager$1(),
response: new InterceptorManager$1(),
};
var InterceptorManager = (function () {
function InterceptorManager() {
this.handlers = [];
}
// ...
// _createClass 方法可以给对象的原型上添加属性
_createClass(InterceptorManager, [
{
key: "use",
value: function use(fulfilled, rejected, options) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected,
synchronous: options ? options.synchronous : false,
runWhen: options ? options.runWhen : null,
});
return this.handlers.length - 1;
},
},
{
key: "forEach",
value: function forEach(fn) {
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
},
},
// ...
]);
return InterceptorManager;
})();
var InterceptorManager$1 = InterceptorManager;
可以看到 interceptors.request
和 interceptors.response
各自指向 InterceptorManager
的实例。
InterceptorManager
原型上设置了 use()
方法,执行后给待执行队列 this.handlers
中添加一条数据。
这里利用了 this
的特性来区分作用域:谁调用 use()
方法就给谁的 handlers
中添加数据。在调用 use()
方法之后,已经将回调函数按顺序添加到了 handlers
数组中。
forEach()
方法用来遍历当前作用域 handlers
中不为 null
的元素,在执行 * 时有用到,详情见下文。
* 如何执行
* 是在调用了 request()
方法前后执行的,先看相关源码:
var Axios = (function () {
_createClass(Axios, [
{
key: "request",
value: function request(configOrUrl, config) {
// ...
// 初始化请求 *
var requestInterceptorChain = [];
var synchronousRequestInterceptors = true;
this.interceptors.request.forEach(function unshiftRequestInterceptors(
interceptor
) {
if (
typeof interceptor.runWhen === "function" &&
interceptor.runWhen(config) === false
) {
return;
}
synchronousRequestInterceptors =
synchronousRequestInterceptors && interceptor.synchronous;
requestInterceptorChain.unshift(
interceptor.fulfilled,
interceptor.rejected
);
});
// 初始化响应 *
var responseInterceptorChain = [];
this.interceptors.response.forEach(function pushResponseInterceptors(
interceptor
) {
responseInterceptorChain.push(
interceptor.fulfilled,
interceptor.rejected
);
});
var promise;
var i = 0;
var len;
// 请求 * 同步执行模式
if (!synchronousRequestInterceptors) {
var chain = [dispatchRequest.bind(this), undefined];
chain.unshift.apply(chain, requestInterceptorChain);
chain.push.apply(chain, responseInterceptorChain);
len = chain.length;
promise = Promise.resolve(config);
console.log(11, chain);
while (i < len) {
promise = promise.then(chain[i++]).catch(chain[i++]);
}
return promise;
}
// 请求 * 异步执行模式
len = requestInterceptorChain.length;
var newConfig = config;
i = 0;
while (i < len) {
var onFulfilled = requestInterceptorChain[i++];
var onRejected = requestInterceptorChain[i++];
try {
newConfig = onFulfilled(newConfig);
} catch (error) {
onRejected.call(this, error);
break;
}
}
try {
promise = dispatchRequest.call(this, newConfig);
} catch (error) {
return Promise.reject(error);
}
i = 0;
len = responseInterceptorChain.length;
while (i < len) {
promise = promise.then(
responseInterceptorChain[i++],
responseInterceptorChain[i++]
);
}
return promise;
},
},
]);
})();
上面是相关的全部代码,下面进行分解。
* 回调方法的添加顺序
var requestInterceptorChain = []; // 请求 * 执行链
// 是否同步执行请求 * ,每个 * 都可以单独设置,但是只有所有 * 都设置为true才为true
var synchronousRequestInterceptors = true;
this.interceptors.request.forEach(function unshiftRequestInterceptors(
interceptor
) {
// 传入 runWhen() 方法时,如果 runWhen() 方法返回值为 false 则忽略这个请求 *
if (
typeof interceptor.runWhen === "function" &&
interceptor.runWhen(config) === false
) {
return;
}
// 是否同步执行
synchronousRequestInterceptors =
synchronousRequestInterceptors && interceptor.synchronous;
// 用 unshift() 添加数据,后设置的 * 在前
requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
});
var responseInterceptorChain = []; // 响应 * 执行链
this.interceptors.response.forEach(function pushResponseInterceptors(
interceptor
) {
// 响应 * 按顺序加在后面
responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
});
interceptors.request.forEach()
的定义在上文提到过,封装为遍历自身的 handlers
数组,与数组的 forEach
类似,只是过滤了值为 null
的元素。
runWhen()
方法接收 config,返回 boolean,控制是否将当前 * 的回调函数添加到执行链中。
请求 * 用 unshift()
方法添加,所以后设置的先执行,响应 * 用 push()
方法添加,所以按照设置顺序执行。
只要有一个 * 的 synchronous
设置为 false
,则 synchronousRequestInterceptors
的值为 false
。
同步执行请求 * (顺序执行)
synchronousRequestInterceptors
为 false
时为同步执行,相关逻辑如下:
var promise;
var i = 0;
var len;
if (!synchronousRequestInterceptors) {
var chain = [dispatchRequest.bind(this), undefined]; // 执行链
chain.unshift.apply(chain, requestInterceptorChain); // 将请求 * 添加到请求之前
chain.push.apply(chain, responseInterceptorChain); // 将响应 * 添加到响应之后
len = chain.length;
promise = Promise.resolve(config);
while (i < len) {
promise = promise.then(chain[i++], chain[i++]); // 用 Promise 包装执行链
}
return promise;
}
dispatchRequest()
是发送请求的方法。
同步执行模式下,会将执行链中的所有方法用 Promise 进行封装,前一个方法执行完毕后将其返回值作为参数传递给下一个方法。
这里的 chain
其实就是所有 * 方法与请求方法合并而成的执行链,等价于: [...requestInterceptorChain, dispatchRequest.bind(this), ...responseInterceptorChain]
。
从一个例子来看 chain
的成员:
axios.interceptors.request.use(onFulfilled1, onRejected1);
axios.interceptors.request.use(onFulfilled2, onRejected2);
axios.interceptors.response.use(onFulfilled3, onRejected3);
axios.interceptors.response.use(onFulfilled4, onRejected4);
axios.get();
// chain: [onFulfilled2, onRejected2, onFulfilled1, onRejected1, dispatchRequest, onFulfilled3, onRejected3, onFulfilled4, onRejected4]
在构建 Promise 链的时候,一次遍历中取了两个方法传递给 then():
promise = Promise.resolve(config);
while (i < len) {
promise = promise.then(chain[i++], chain[i++]); // 用 Promise 包装执行链
}
return promise;
异步执行请求 * (同时执行)
var promise;
var i = 0;
var len = requestInterceptorChain.length; // 请求执行链的长度
var newConfig = config;
i = 0;
// 执行请求 * 回调
while (i < len) {
var onFulfilled = requestInterceptorChain[i++]; // use() 的第一个参数
var onRejected = requestInterceptorChain[i++]; // use() 的第二个参数
// 执行成功后继续执行,执行失败后停止执行。
try {
newConfig = onFulfilled(newConfig);
} catch (error) {
onRejected.call(this, error);
break;
}
}
// 执行发送请求方法
try {
promise = dispatchRequest.call(this, newConfig);
} catch (error) {
return Promise.reject(error); // 执行失败后退出
}
// 执行响应 * 回调
i = 0;
len = responseInterceptorChain.length;
while (i < len) {
promise = promise.then(
responseInterceptorChain[i++], // use() 的第一个参数
responseInterceptorChain[i++] // use() 的第二个参数
);
}
return promise;
dispatchRequest()
是发送请求的方法。
用 while
循环遍历所有的请求 * 并调用,将执行语句包裹在 try-catch
语句中,只要有一个请求 * 异常就停止循环。
可以看到在异步模式下,请求 * 为异步执行,但是不影响发送请求,而响应 * 还是在请求响应后同步执行。
Q&A
* 是如何工作的
调用 .request.use()
和 .response.use()
方法时,将传入的 * 回调方法分别存入 请求 * 回调数组
和 响应 * 回调数组
。
在调用 .get()
等方法时,将 请求 * 回调数组
和 响应 * 回调数组
与发送请求的方法拼接成一个完整的执行链,按照同步或异步的模式调用执行链中的方法。
响应 * 总是在返回响应结果后按顺序执行。
请求 * 根据 synchronous
配置不同,行为有所不同:
异步执行请求 * 模式。
没有设置
synchronous
时默认为异步执行请求 * 模式,即遍历执行所有的请求 * 一参回调,执行报错后停止遍历,并执行该 * 的二参回调。同步执行请求 * 模式。
设置
synchronous
为false
时为同步执行请求 * 模式,将执行链包装成一个 Promise 链顺序执行。
* 的执行顺序
先执行请求 * ,后执行响应 * 。
设置多个请求 * 的情况:后添加的 * 先执行。
设置多个响应 * 的情况:按照设置顺序执行。
同步&异步
计算机中的同步,指的是现实中的一步一步(同一时间只能干一件事),异步指的是同时进行(同一时间能干多件事)。
参考 npm axios
来源:https://juejin.cn/post/7198326957108838455
猜你喜欢
- --分页存储过程示例 Alter PROCEDURE [dbo].[JH_PageDemo] @pageSize int = 9000000
- 1、python安装可以跨平台2、有两个版本2.7和3.6,第三方库适用2.7版,两个版本不兼容windows安装:第一种方法官网安装:在官
- 1.安装python3yum -y install wget gcc make zlib-devel readline-devel bzip
- 我一般都是通过xpath解析DOM树的时候会使用lxml的etree,可以很方便的从html源码中得到自己想要的内容。这里主要介绍一下我常用
- 常用的重定向方式有: 301 redirect, 302 redirect 与 meta fresh:301 redirect: 301代表
- 切片主要用于序列对象中,按照索引区间截取出一段索引的内容。切片的书写形式:[i : i+n : m] ;其中,i 是切片的起始索引值,为列表
- 本文实例讲述了python实现多进程按序号批量修改文件名的方法。分享给大家供大家参考,具体如下:说明文件名命名方式如图,是数字序号开头,但是
- 简介pip 是 Python 的包安装程序。其实,pip 就是 Python 标准库(The Python Standard Library
- 本文实例讲述了Python使用matplotlib简单绘图。分享给大家供大家参考,具体如下:# -*- coding:utf-8 -*-#!
- 在我们的算法中,有一种叫做线性查找。分为:顺序查找。 折
- 1、panic当我们执行panic的时候会结束下面的流程:package mainimport "fmt"func ma
- 一、读取Excel中的数据安装xlrd 只能读取Excel内容pip install xlrd==1.2.0xlrd库的open_workb
- Redisredis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串
- 摘要:MySQL JDBC抽取到底应该采用什么样的方式,且听小编给你娓娓道来。小编最近在云上的一个迁移项目中被MySQL抽取模式折磨的很惨。
- 本文实例讲述了Python实现求解一元二次方程的方法。分享给大家供大家参考,具体如下:1. 引入math包2. 定义返回的对象3. 判断b*
- 作用:pygame一般用来做游戏注意:1.在使用pygame提供的功能之前,需要调用init方法2.在游戏结束前需要调用 quit 方法py
- 本文实例讲述了python实现从ftp服务器下载文件的方法。分享给大家供大家参考。具体实现方法如下:import ftplibftp = f
- 有一编文章是用JavaScript对XML文件操作来实现无限级联动菜单的,我们可结合ASP来完成对数据库值的读取,然后写入XML文件,再用J
- 一、避免Firefox 背景图不显示的兼容问题,定义background 属性,先后顺序不能随意变动。background : backgr
- Python的3.0版本,常被称为Python 3000,或简称Py3k。相对于Python的早期版本,这是一个较大的升级。为了不带入过多的