Самая полная и подробная интерпретация исходного кода Axios --- достаточно прочесть эту статью

внешний интерфейс исходный код
Самая полная и подробная интерпретация исходного кода Axios --- достаточно прочесть эту статью

Аксиос - это бог-конь 🐎?

axiosна основеPromiseсправлятьсяhttpКраткая, простая в использовании и эффективная библиотека-оболочка кода для запросов. С точки зрения непрофессионала, это замена переднего концаAjaxВещь, которую можно использовать для инициации функции интерфейса http-запроса, она основана наPromise, в сравнении сAjaxФункция обратного вызова может лучше управлять асинхронными операциями.Адрес источника

Основные характеристики Axios

  • Обещание на основе
  • Поддерживает среды браузера и node.js.
  • Возможность добавлять перехватчики запросов и ответов и преобразовывать данные запросов и ответов.
  • Запросы могут быть отменены, прерваны
  • Автоматически преобразовывать данные JSON
  • Поддержка клиентов против XSRF

Структура каталогов исходного кода и описание основных файловНа основе версии 0.21.4

├── /lib/                          // 项目源码目
  └── /adapters/                     // 定义发送请求的适配器
      ├── http.js                       // node环境http对象
      ├── xhr.js                        // 浏览器环境XML对象
  └── /cancel/                       // 定义取消请求功能
  └── /helpers/                      // 一些辅助方法
  └── /core/                         // 一些核心功能
      ├──Axios.js                      // axios实例构造函数                 
      ├── createError.js               // 抛出错误
      ├── dispatchRequest.js           // 用来调用http请求适配器方法发送请求
      ├── InterceptorManager.js        // 拦截器管理器
      ├── mergeConfig.js               // 合并参数
      ├── settle.js                    // 根据http响应状态,改变Promise的状态
      ├── transformData.js             // 转数据格式
 └── axios.js                        // 入口,创建构造函数
 └── defaults.js                     // 默认配置
 └── utils.js                        // 公用工具函数

от входа

мы открыты/lib/axios.js, начните анализ со входа.

var utils = require('./utils');
var bind = require('./helpers/bind');
var Axios = require('./core/Axios');
var mergeConfig = require('./core/mergeConfig');
var defaults = require('./defaults');

// 创建axios实例的方法
function createInstance(defaultConfig) {
  // 根据默认配置构建个上下文对象,包括默认配置和请求、响应拦截器对象
  var context = new Axios(defaultConfig);
  // 创建实例 bind后返回的是一个函数,并且上下文指向context
  var instance = bind(Axios.prototype.request, context);
  // 拷贝prototype到实例上 类似于把Axios的原型上的方法(例如: request、get、post...)继承到实例上,this指向为context
  utils.extend(instance, Axios.prototype, context);
  // 拷贝上下文对象属性(默认配置和请求、相应拦截器对象)到实例上, this指向为context
  utils.extend(instance, context);
  
  // 创建axios实例,一般axios封装 应该都会用到 (我们把一些默认、公共的配置都放到一个实例上,复用实例,无需每次都重新创建实例)
  instance.create = function create(instanceConfig) {
    // 这里mergeConfig 就是用来深度合并的
    return createInstance(mergeConfig(defaultConfig, instanceConfig));
  };

	// 返回实例
  return instance;
}

// 创建实例 defaulst为默认配置 
var axios = createInstance(defaults);

// 向外暴露Axios类,可用于继承 (本人暂未使用过)
axios.Axios = Axios;

// 这里抛出 中断/取消请求的相关方法到入口对象
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');

// 并发请求 完全就是用promise的能力
axios.all = function all(promises) {
  return Promise.all(promises);
};
// 和axios.all 共同使用,单个形参数组参数转为多参 =====> 后面有详解!!!
axios.spread = require('./helpers/spread');

// 用作监测是否为Axios抛出的错误
axios.isAxiosError = require('./helpers/isAxiosError');
// 导出
module.exports = axios;

// 允许在ts中使用默认导出
module.exports.default = axios;

createInstance

Анализируя входной файл, мы можем найти:

  • Обычно мы используем его непосредственно в разработкеaxios.create()встроенный экземпляр и прямойaxios(), на всем протяженииcreateInstanceстроится по этой функции.

Эта функция примерно выполняет следующие действия:

  1. Во-первых, создать объект контекста на основе конфигурации по умолчанию, включая конфигурацию и запрос по умолчанию, а также соответствующий объект-перехватчик.
  2. После создания экземпляра привязка возвращает функцию, поэтому, когда мы ее используем, мы можем axios(config) Это используется, и контекст указывает на контекст
  1. Копирование прототипа в экземпляр аналогично наследованию методов прототипа Axios (например: request, get, post...) в экземпляр, только при использовании axios.get(), axios.post() , это указывает на контекст
  2. Скопируйте свойства объекта контекста (конфигурация и запрос по умолчанию, соответствующий объект-перехватчик) в экземпляр, это указывает на контекст
  1. Возвращает экземпляр (экземпляр — это функция)

axios.create

противaxios.createметод, заканчивая и пишу эту аналитическую статью, я обнаружил, что в5 сентября 2021 г.С таким обновлением PR, зачем вы это делаете: это для крупномасштабных приложений, или в случае использования нескольких экземпляров в нескольких доменах, вы можете повторно инкапсулировать конструкцию для уже построенных экземпляров, чтобы обеспечить возможность глубокого построить контроллеры:Смотрите этот PR для деталей

  • axiosчасто используемыхapiМетод запроса связан следующей строкой кодаaxiosВверх.

по которому мы прибываем/lib/core/Axios.jsпосмотриAxios.prototypeЧто висит на:

// 主请求 方法 所有请求最终都会指向这个方法
Axios.prototype.request =  function request(config) { } //内容后面详解
// 获取完成的请求url方法
Axios.prototype.getUri = function getUri(config) { };

// 这里将普通请求(无body数据)挂到prototype上
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  Axios.prototype[method] = function(url, config) {
    // 最终都调用request方法
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: (config || {}).data
    }));
  };
});
// 这里将有body数据的请求挂到prototype上
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  Axios.prototype[method] = function(url, data, config) {
    // 最终都调用request方法
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

Axios.prototypeНавешано девять методов, в том числе и наше общее следование этим методам.

axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])

Метод запроса здесь делится на два типа: обход и зависание.prototypeВкл, потому что три метода последней стороны могут иметь тело запроса, а форма отличается, поэтому ее необходимо разделить.


Axios.prototype.request

Далее мы углубимся в основной метод запроса.Axios.prototype.requestВыше можно сказать, что этот метод является основным скелетом всего запроса axios, который в основном адаптируется к различным конфигам, а также ключевым ядром.цепной вызоввыполнить. Мы ввели код, чтобы увидеть:

Axios.prototype.request = function request(config) {
  // 判断参数类型 以支持不同的请求形式axios('url',config) / axios(config)
  if (typeof config === 'string') {
    config = arguments[1] || {};
    config.url = arguments[0];
  } else {
    config = config || {};
  }
	// 配置合并默认配置
  config = mergeConfig(this.defaults, config);

  // 转化请求的方法 转化为小写
  if (config.method) {
    config.method = config.method.toLowerCase();
  } else if (this.defaults.method) {
    config.method = this.defaults.method.toLowerCase();
  } else {
    config.method = 'get';
  }

  var transitional = config.transitional;
	
  if (transitional !== undefined) {
    // 针对性配置检测 1.0.0版本以后 transitional配置将移除 (好奇目前距离1.0版本好像距离很远,不知为何)
    validator.assertOptions(transitional, {
      silentJSONParsing: validators.transitional(validators.boolean, '1.0.0'),
      forcedJSONParsing: validators.transitional(validators.boolean, '1.0.0'),
      clarifyTimeoutError: validators.transitional(validators.boolean, '1.0.0')
    }, false);
  }
	
  // ........ 下面的内容有比较大的更新,单独拆出来详解!!!!
 
};

Состав цепочки обещаний

Давайте взглянем на исходный классический код, составляющий цепочку промисов:

  // 创建存储链式调用的数组 首位是核心调用方法dispatchRequest,第二位是空
  var chain = [dispatchRequest, undefined];
  // 创建 promise 为什么resolve(config)是因为 请求拦截器最先执行 所以 设置请求拦截器时可以拿到每次请求的所有config配置
  var promise = Promise.resolve(config);
  // 把设置的请求拦截器的成功处理函数、失败处理函数放到数组最前面
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
  // 把设置的响应拦截器的成功处理函数、失败处理函数放到数组最后面
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });
  // 循环 每次取两个出来组成promise链.then执行
  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }
  // 返回promise 
  return promise;

Используйте диаграмму для описания приведенного выше кода, чтобы он был понятнее.Всю цепочку промисов можно понимать как выполнение слева направо:

Перехватчик запроса ===> Запрос ===> Перехватчик ответа


новый пиар

Скелет цепного вызова Вот новый pr 6 месяцев назад, рефакторинг этой части логики кода,Этот PR-контент очень большой, пожалуйста, потерпите его:

Это в основном для асинхронной ситуации перехватчика запросов, или выполнения длинной макрозадачи, и рефакторинга предыдущего кода, потому что запрос выполняется в микрозадаче, и время создания микрозадачи для создания промиса. Перед цепочкой, если задача макроса занимает много времени до выполнения запроса или если перехватчик запроса является асинхронным, это приведет к реальномуajaxБудет определенная задержка по времени отправки запроса, поэтому необходимо решить эту проблему.

  // 请求拦截器储存数组
  var requestInterceptorChain = [];
  // 默认所有请求拦截器都为同步
  var synchronousRequestInterceptors = true;
  // 遍历注册好的请求拦截器数组
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    // 这里interceptor是注册的每一个拦截器对象 axios请求拦截器向外暴露了runWhen配置来针对一些需要运行时检测来执行的拦截器
    // 如果配置了该函数,并且返回结果为true,则记录到拦截器链中,反之则直接结束该层循环
    if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
      return;
    }
    // interceptor.synchronous 是对外提供的配置,可标识该拦截器是异步还是同步 默认为false(异步) 
    // 这里是来同步整个执行链的执行方式的,如果有一个请求拦截器为异步 那么下面的promise执行链则会有不同的执行方式
    synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
    // 塞到请求拦截器数组中
    requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
  // 相应拦截器存储数组
  var responseInterceptorChain = [];
  // 遍历按序push到拦截器存储数组中
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
  });

  var promise;
  // 如果为异步 其实也是默认情况
  if (!synchronousRequestInterceptors) {
    // 这里和重构之前的逻辑是一致的了
    var chain = [dispatchRequest, undefined];
    // 请求拦截器塞到前面
    Array.prototype.unshift.apply(chain, requestInterceptorChain);
    // 响应拦截器塞到后面
    chain = chain.concat(responseInterceptorChain);
    promise = Promise.resolve(config);
    // 循环 执行
    while (chain.length) {
      promise = promise.then(chain.shift(), chain.shift());
    }
    // 返回promise
    return promise;
  }

  // 这里则是同步的逻辑 
  var newConfig = config;
  // 请求拦截器一个一个的走 返回 请求前最新的config
  while (requestInterceptorChain.length) {
    var onFulfilled = requestInterceptorChain.shift();
    var onRejected = requestInterceptorChain.shift();
    // 做异常捕获 有错直接抛出
    try {
      newConfig = onFulfilled(newConfig);
    } catch (error) {
      onRejected(error);
      break;
    }
  }
  // 到这里 微任务不会过早的创建 也就解决了 微任务过早创建、当前宏任务过长或某个请求拦截器中有异步任务而阻塞真正的请求延时发起问题
  try {
    promise = dispatchRequest(newConfig);
  } catch (error) {
    return Promise.reject(error);
  }
  // 响应拦截器执行
  while (responseInterceptorChain.length) {
    promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift());
  }

  return promise;

/core/InterceptorManager.js

// 拦截器增加两个配置参数 synchronous、 runWhen
InterceptorManager.prototype.use = function use(fulfilled, rejected, options) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected,
    // 默认情况下它们被假定为异步的 如果您的请求拦截器是同步的,可以通过这个参数默认配置,它将告诉 axios 同步运行代码并避免请求执行中的任何延迟。
    synchronous: options ? options.synchronous : false,
    // 如果要基于运行时检查执行特定拦截器,可以通过这个runWhen这个参数,类型为函数 
    runWhen: options ? options.runWhen : null
  });
  return this.handlers.length - 1;
};

Вышеприведенный контент нуждается в повторной разборке.Автор также тщательно проанализировал исходный код и обсуждение PR рефакторинга:Смотрите этот PR для деталей !!!


Сравнение конкретных изменений

Реализация перехватчика

В нашем реальном использовании axios часто используются запросы и соответствующие перехватчики, что также является одной из характеристик axios. Выше мы проанализировали состав цепочки промисов, когда был создан перехватчик, мы находимся вaxios.createсерединаcreateInstanceидтиnew AxiosСоздается конструкция времени экземпляра. Перейдите непосредственно к коду:

function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  这里创建的请求和响应拦截器 通过统一的类构造出来的
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

Входим в /core/InterceptorManager.js:

function InterceptorManager() {
  this.handlers = [];
}
// 添加拦截器 添加成功、失败回调
InterceptorManager.prototype.use = 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;
};

// 注销指定拦截器
InterceptorManager.prototype.eject = function eject(id) {
  if (this.handlers[id]) {
    this.handlers[id] = null;
  }
};

// 遍历执行
InterceptorManager.prototype.forEach = function forEach(fn) {
  utils.forEach(this.handlers, function forEachHandler(h) {
    // 确定没被eject注销 才执行
    if (h !== null) {
      fn(h);
    }
  });
};

module.exports = InterceptorManager;

Реализация перехватчика относительно проста.Благодаря унифицированной модели создается единый контроллер для управления регистрацией, отменой и выполнением перехватчика.

dispatchRequest

Добираемся до основного метода запросаdispatchRequest, структура здесь на самом деле выглядит проще:

    • Конфигурация конфигурации заголовка запроса обработки
    • передачаadapterАдаптер инициирует реальный запрос, инициирует запрос ajax для среды браузера и инициирует запрос http в среде узла.
    • Создайте данные ответа, данные JSON будут автоматически преобразованы
 function dispatchRequest(config) {
	// 提前取消请求
  throwIfCancellationRequested(config);

  // 赋个默认值
  config.headers = config.headers || {};

  // 转换数据
  config.data = transformData.call(
    config,
    config.data,
    config.headers,
    config.transformRequest
  );

  // 合并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];
    }
  );
  // 适配器 axios是可以支持node端也支持浏览器端的 
  var adapter = config.adapter || defaults.adapter;
  // 执行请求
  return adapter(config).then(function onAdapterResolution(response) {
    // 提前取消请求情况
    throwIfCancellationRequested(config);
    // 做数据转换
    response.data = transformData.call(
      config,
      response.data,
      response.headers,
      config.transformResponse
    );
    return response;
  }, function onAdapterRejection(reason) {
    if (!isCancel(reason)) {
      throwIfCancellationRequested(config);
      // 做数据转换
      if (reason && reason.response) {
        reason.response.data = transformData.call(
          config,
          reason.response.data,
          reason.response.headers,
          config.transformResponse
        );
      }
    }
    return Promise.reject(reason);
  });
};

адаптер адаптер

Классический шаблон проектирования: применен шаблон адаптера.

function getDefaultAdapter() {
  var adapter;
  // 判断XMLHttpRequest对象是否存在 存在则代表为浏览器环境
  if (typeof XMLHttpRequest !== 'undefined') {
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');
    // node环境 使用原生http发起请求
  } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    adapter = require('./adapters/http');
  }
  return adapter;
}

./adapters/xhr.js — это родной AjaxXMLHttpRequestИнкапсуляция объекта, ./адаптеры/http.js это NodehttpИнкапсуляция модуля также будет обработана соответственно для HTTPS. Специальные детали инкапсуляции и различные граничные детали были специально обработаны, потому что мы все еще используем больше на стороне браузера в нашей повседневной жизни.xhrИсходный код пакета, чтобы сделать некоторые общие.

axios активно отменяет запрос

как использовать

Здесь мы сначала рассмотрим, как используется следующий запрос на отмену:

import { CancelToken } from axios;

// source为一个对象 结构为 { token, cancel }
// token用来表示某个请求,是个promise
// cancel是一个函数,当被调用时,则取消token注入的那个请求
const source = CancelToken.source();

axios
    .get('/user', {
        // 将token注入此次请求
        cancelToken: source.token, 
    })
    .catch(function (thrown) {
        // 判断是否是因为主动取消而导致的
        if (axios.isCancel(thrown)) {
            console.log('主动取消', thrown.message);
        } else {
            console.error(thrown);
        }
    });
// 这里调用cancel方法,则会中断该请求 无论请求是否成功返回
source.cancel('我主动取消请求')

Анализ исходного кода

существуетlib/axios.js axiosЭкземпляр отбрасывает три связанных интерфейса для запросов на отмену.Давайте посмотрим на три файла, участвующих в запросах на отмену. /lib/cancel/ , соответствующие роли:

1.Cancel.js : CancelФункция (класс подделки), приняв параметр сообщения, на самом деле вызываетsource.cancel()Параметры в: информация об отмене, об объекте-прототипе__CANCEL__Атрибут предназначен для идентификации информации, возвращаемой запросом на изменение, для информации, возвращаемой запросом на отмену.

2.CancelToken.js:CancelTokenОбеспечьте возможность создания экземпляра токена для регистрации запроса на отмену и предоставления метода запроса на отмену.

3.isCancel.js: используется для определения того, является ли результат возвращенным для запроса на отмену, то есть является ли это экземпляром Cancel.

Давайте проанализируем основныеCancelTokenИсходный код , проанализированный с точки зрения исполнения:

1. исходный метод

// 暴露出token 和 cancel取消方法
CancelToken.source = function source() {
  var cancel;
  // 构造CancelToken 的实例,实例上有两个属性一个promise一个reason
  // 同时把注册的回调函数的参数也是个函数把这个函数的执行权抛使用者调用(cancel)
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  return {
    token: token,
    cancel: cancel
  };
};

В объекте, возвращаемом исходным методом, есть два свойства:tokenдляnew CancelTokenэкземпляр ,cancelДа Даnew CancelTokenфункция времениexecutorПараметр , — это функция, используемая для вызова активного запроса на отмену при необходимости. Давайте проанализируемCancelTokenисходный код.

2. Конструктор Canceltoken

function CancelToken(executor) {
  // 类型判断
  if (typeof executor !== 'function') {
    throw new TypeError('executor must be a function.');
  }
  // 创建一个promise的实例
  var resolvePromise;
  this.promise = new Promise(function promiseExecutor(resolve) {
  // 把resolve方法提出来 当resolvePromise执行时,this.promise状态会变为fulfilled
    resolvePromise = resolve;
  });
  // 存一下this
  var token = this;
  // new CancelToken时会立即调用executor方法 也就是 会执行source方法中的cancel = c;
  // 这里也就是把cancel函数暴露出去了,把取消的时机留给了使用者 使用者调用cancel时候也就会执行函数内的逻辑
  executor(function cancel(message) {
    // 请求已经被取消了直接return
    if (token.reason) {
      return;
    }
		// 给token(可就是当前this上)添加参数 调用new Cancel构造出cancel信息实例
    token.reason = new Cancel(message);
    // 这里当主动调用cancel方法时,就会把this.promise实例状态改为fulfilled,resolve出的信息则是reason(new Cancel实例)
    resolvePromise(token.reason);
  });
}

Вот краткое изложение, вCancelTokenсоздастpromiseЭкземпляр и причина отмены хранения информации при вызове пользователяsource.cancel(message)метод,promiseСостояние инстанса меняется на выполненный, и при этом по параметрамmessageСоздайтеreasonЭкземпляр сообщения об ошибке, также есть__CANCEL__Атрибут, который идентифицирует его как информацию, возвращенную запросом на отмену.

3. Как обрабатывается запрос! ! !

Операции в адаптере

когда мы звонимcancelПосле метода, как нам прервать/отменить запрос в запросе?Такой кусок кода в адаптере может найти нужный нам ответ.Адрес источника

// 判断使用者在改请求中是否配置了取消请求的token
if (config.cancelToken) {
  // 如果配置了则将实例上的promise用.then来处理主动取消调用cancel方法时的逻辑 
  // 也就是说如果ajax请求发送出去之前,这时我们已经给cancelToken的promise注册了.then
  // 当我们调用cancel方法时,cancelToken实例的promise会变为fulfilled状态,.then里的逻辑就会执行
  config.cancelToken.promise.then(function onCanceled(cancel) {
    if (!request) {
      return;
    }
    // 调用 原生abort取消请求的方法
    request.abort();
    // axios的promise实例进入rejected状态 这里我们可以看到主动取消的请求是catch可以捕获到
    reject(cancel);
    // request置为null
    request = null;
  });
}
// 真正的请求在这时才发送出去!!!
request.send(requestData);

Выше показано, как наш axios прерывает запрос в запросе.В других случаях логика отмены также может быть сделана заранее до запроса и после завершения запроса, что также позволяет избежать избыточной отправки запроса и выполнения ненужной логики.Давайте посмотрим как это сделано. Давайте взглянемCancelTokenна прототипеthrowIfRequestedметод:

// CancelToken原型上有个么一个方法 很简单就是直接抛错 将reason抛出
// reason则是根据调用cancel函数的参数 new Cancel的实例
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
  if (this.reason) {
    throw this.reason;
  }
};

в нашем основном методе запросаdispatchRequestсередина:

Непосредственная выдача ошибки означает, что состояние экземпляра обещания, созданного axios, будет напрямую установлено как отклоненное, поэтому логика .catch напрямую следует.

// 判断如果配置了取消请求的token则就抛出
function throwIfCancellationRequested(config) {
  if (config.cancelToken) {
    // 调用抛出错误的方法
    config.cancelToken.throwIfRequested();
  }
}

module.exports = function dispatchRequest(config) {
  // 请求前
  throwIfCancellationRequested(config);
  // ... 省略代码
  // 请求中的在上面adapter中
  return adapter(config).then(function onAdapterResolution(response) {
    // 请求完成后
    throwIfCancellationRequested(config);
    // ... 省略代码
  }, function onAdapterRejection(reason) {
    // 请求完成后
    if (!isCancel(reason)) {
      throwIfCancellationRequested(config);
      // ... 省略代码
    }
    return Promise.reject(reason);
  });
};

мы тамaxiosзапрос вcatchпрошедшийisCancelМетод определяет, вызвано ли исключение запросом на отмену, то есть определяет, является ли это экземпляром Cancel, и обрабатывает его соответствующим образом.


Давайте лучше разберемся, как отменяется запрос через краткий процесс запроса:

Я считаю, что после приведенного выше анализа исходного кода и анализа блок-схемы вы должны иметь приблизительное представление о принципе отмены запроса, и вам необходимо неоднократно читать его, чтобы прояснить весь процесс выполнения и детали.

другие точки

Возможность параллелизма

в официальномaxios, также обеспечиваетaxios.allа такжеaxios.spreadЭти два метода в основном используются для выполнения нескольких одновременных запросов.

function getUserAccount() {
  return axios.get('/user/12345');
}

function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread((acct, perms) => {
    // 两个请求都完成后才会执行回调里的逻辑
  }));

Давайте посмотрим непосредственно на исходный код:

1.axios.allметод сPromise.allМетод полностью идентичен, называется прямымPromise.all

2.axios.spreadВ качестве параметра метод получает функцию, а параметр этой функции-параметра также является ответом на все запросы

// 并发请求 完全就是用promise的能力
axios.all = function all(promises) {
  return Promise.all(promises);
};
// 接受一个函数callback
axios.spread = function spread(callback) {
  // 返回一个新函数 arr其实就是成功返回的数组
  return function wrap(arr) {
    // 把并发请求的返回结果给callback 方便把并发请求返回的数据放在一起做处理像上面例子那样
    return callback.apply(null, arr);
  };
};

Вспомогательная функция

mergeФункция: рекурсивное разделение, используемое для слияния некоторой информации о конфигурации запроса, метод реализации фактически такой же, как и рекурсивное глубокое копирование.deepCloneаналогичный

function merge(/* obj1, obj2, obj3, ... */) {
  var result = {};
  // 闭包处理逻辑函数
  function assignValue(val, key) {
    // result里有该键值并且 同为普通Object对象类型递归merge
    if (isPlainObject(result[key]) && isPlainObject(val)) {
      result[key] = merge(result[key], val);
      // result里没有 赋值
    } else if (isPlainObject(val)) {
      result[key] = merge({}, val);
      // 数组类型
    } else if (isArray(val)) {
      result[key] = val.slice();
      // 其他类型直接赋值
    } else {
      result[key] = val;
    }
  }
	// 循环入参调用
  for (var i = 0, l = arguments.length; i < l; i++) {
    forEach(arguments[i], assignValue);
  }
  // 返回合并后的结果
  return result;
}

extendфункция:axiosОн используется внутри для подключения некоторых встроенных свойств и встроенных методов к экземпляру оси.

function extend(a, b, thisArg) {
  // 循环 b的属性挂到a上
  forEach(b, function assignValue(val, key) {
    // 如果有具体this指向 并且类型为函数
    if (thisArg && typeof val === 'function') {
      // bind函数调用完返回了一个函数,这个函数内部使用的apply
      a[key] = bind(val, thisArg);
    } else {
      // 直接赋值
      a[key] = val;
    }
  }); 
  return a;
}

Суммировать

Эта статья дляaxiosОсновной исходный код .понять быстроaxiosИсходный код и лучшее использование в последующей разработкеaxios.


напиши в конце

Если в статье есть ошибки, вы можете исправить их в комментариях, если статья вам помогла, ставьте лайк 👍 и подписывайтесь на ❤️