一、环境准备
-
axios版本v0.24.0 -
通过
github1s网页可以 查看 axios - dispatchRequest.js 源码 -
调试需要
clone到本地
git clone https://github.com/axios/axios.git
cd axios
npm start
http://localhost:3000/
二、函数研读
1. 辅助函数总览
"use strict";
var utils = require("./../utils");
var transformData = require("./transformData");
var isCancel = require("../cancel/isCancel");
var defaults = require("../defaults");
var Cancel = require("../cancel/Cancel");
2. 辅助函数 transformData
"use strict";
var utils = require("./../utils");
var defaults = require("./../defaults");
/**
* @param {Object|String} data The data to be transformed
* @param {Array} headers The headers for the request or response
* @param {Array|Function} fns A single function or Array of functions
* @returns {*} The resulting transformed data
*/
module.exports = function transformData(data, headers, fns) {
var context = this || defaults;
/*eslint no-param-reassign:0*/
utils.forEach(fns, function transform(fn) {
data = fn.call(context, data, headers);
});
return data;
};
===》调用地点
config.data = transformData.call(
config,
config.data,
config.headers,
config.transformRequest
);
===》config.transformRequest 配置地点 default.js
transformResponse: [
function transformResponse(data) {
...
if (
strictJSONParsing ||
(forcedJSONParsing && utils.isString(data) && data.length)
) {
try {
return JSON.parse(data);
} catch (e) {
...
}
}
return data;
},
],
- 声明全局变量
context从当前上下文this或者实例defaults获取全局上下文 - 调用
utils中的forEach,fns中的每一个函数fn都会被回调触发执行其内部逻辑,每个fn的入参都是data和headers - 从上下文看其实就是用
JSON.parse()处理了一下data,headers就没使用到
Tips:这一块需要结合上下文一起看,否则从命名和功能分析上看知道是个处理日期的工具函数,但是具体的处理逻辑是什么?执行到哪里才会处理?就不清楚了,这也是读源码比较费劲的地方,所以一定要联系上下文。
3. 内部函数 throwIfCancellationRequested
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
if (config.signal && config.signal.aborted) {
throw new Cancel("canceled");
}
}
- 第一个判断
cancelToken是否被实例化,如果是就执行实例的throwIfRequested方法,通过上一篇文章我们知道cancelToken就是一个发布订阅函数,一旦被实例化会立即执行内部的订阅者事件cancel,如果是首次取消会通过resolvePromise返回一个Cancel实例 - 第二个判断是针对
fetch API做的支持,这一点还没有在文档中体现,但可以在axios-README-line740看到使用示例
4. 分发函数 dispatchRequest
/**
* @param {object} config The config that is to be used for the request
* @returns {Promise} The Promise to be fulfilled
*/
module.exports = function dispatchRequest(config) {
throwIfCancellationRequested(config);
// Ensure headers exist
config.headers = config.headers || {};
// Transform request data
config.data = transformData.call(
config,
config.data,
config.headers,
config.transformRequest
);
// Flatten headers
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers
);
utils.forEach(
["delete", "get", "head", "post", "put", "patch", "common"],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
var adapter = config.adapter || defaults.adapter;
return adapter(config).then(
function onAdapterResolution(response) {
throwIfCancellationRequested(config);
// Transform response data
response.data = transformData.call(
config,
response.data,
response.headers,
config.transformResponse
);
return response;
},
function onAdapterRejection(reason) {
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
// Transform response data
if (reason && reason.response) {
reason.response.data = transformData.call(
config,
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);
}
);
};
-
调用
throwIfCancellationRequested,如果用户主动取消请求会直接resolve或者reject,不再执行后续逻辑 -
调用
transformData转换请求头中的日期格式,.call传递当前上下文config -
调用
utils中的merge函数将headers对象信息便平化处理(可以用 ES6 中数组的flat方法理解) -
调用
utils中的forEach函数,每次遍历给定数组元素都会触发cleanHeaderConfig方法,该方法会删除多余的被合并过的headers配置 -
定义全局
adapter接受当前实例的适配器 -
返回适配器执行结果,由于适配器内发送异步请求,需要在链式调用
.then()中拿到返回值,并且在这个过程中需要对resolved或reject值做一些处理,处理逻辑分别在onAdapterResolution和onAdapterRejection中 -
在
onAdapterResolution和onAdapterRejection中均调用了throwIfCancellationRequested,用于检测用户是否在此时触发了取消请求的动作。那么有问题了-请求都已经发过了,甚至结果都返回了还再做这些干啥呢?答,为了不再执行后续的逻辑,可以提升性能。
Tips:关于数组的扁平化可以参考阮一峰老师的-es6 入门
Tips: 不用过多的纠结适配器的概念,后面的文章会深入分析[Browser、node]的适配器
三、总结
请求分发模块的逻辑相对是比较简单的,复杂的地方可能就在throwIfCancellationRequested的调用以及承接上下文上了。throwIfCancellationRequested个人理解就是用户主动取消请求时的出口之一,这个出口的执行时间点是请求已经发送还未响应或者已经响应时。对比之前的cancel模块,那里的出口是在执行器executor内,发生在客户端的ajax请求以及node的http建立前或者是请求结束后的重复请求时,执行时间上会早于或者晚于当前DispatchRequest内的这个出口。
四、参考
1. 仙凌阁的文章详细 Axios 源码解读
2. 林景宜的文章林景宜的记事本 - Axios 源码解析(五):核心工具方法
3. 若川的文章学习 axios 源码整体架构,打造属于自己的请求库
4. 杰凌的文章深入解读 axios 源码










