axios 源码深入分析之 源码实现分析

一、源码实现分析

  1. axios 源码目录结构,如下所示:

    ├── /dist/                     # 项目输出目录
    ├── /lib/                      # 项目源码目录
    │ ├── /adapters/               # 定义请求的适配器 xhr、http
    │ │ ├── http.js                # 实现http适配器(包装http包)
    │ │ └── xhr.js                 # 实现xhr适配器(包装xhr对象)
    │ ├── /cancel/                 # 定义取消功能
    │ ├── /core/                   # 一些核心功能
    │ │ ├── Axios.js               # axios的核心主类
    │ │ ├── dispatchRequest.js     # 用来调用http请求适配器方法发送请求的函数
    │ │ ├── InterceptorManager.js  # 拦截器的管理器
    │ │ └── settle.js              # 根据http响应状态,改变Promise的状态
    │ ├── /helpers/                # 一些辅助方法
    │ ├── axios.js                 # 对外暴露接口
    │ ├── defaults.js              # axios的默认配置 
    │ └── utils.js                 # 公用工具
    ├── package.json               # 项目信息
    ├── index.d.ts                 # 配置TypeScript的声明文件
    └── index.js                   # 入口文件
    
  2. axiosAxios 的关系,如下所示:

  • 从语法上来说: axios 不是 Axios 的实例
  • 从功能上来说: axiosAxios 的实例
  • axios 函数对应的是 Axios.prototype.request 方法通过 bind(Axiox的实例) 产生的函数
  • axios 作为对象有 Axios 原型对象上的所有方法, 有 Axios 对象上所有属性
  • axiosAxios 原型上的所有发特定类型请求的方法: get()/post()/put()/delete()
  • axiosAxios 的实例上的所有属性:defaults/interceptors,后面又添加了 create()/CancelToken()/all()
  1. instanceaxios 的区别? 如下所示:
  • 相同点:
    • 都是一个能发任意请求的函数: request(config)
    • 都有发特定请求的各种方法: get()/post()/put()/delete()
    • 都有默认配置和拦截器的属性: defaults/interceptors
  • 不同点:
    • 默认匹配的值很可能不一样
    • instance 没有 axios 后面添加的一些方法: create()/CancelToken()/all()
  1. 看一下 axios 的核心源码,代码如下所示:
'use strict';

var utils = require('./../utils');
var buildURL = require('../helpers/buildURL');
var InterceptorManager = require('./InterceptorManager');
var dispatchRequest = require('./dispatchRequest');
var mergeConfig = require('./mergeConfig');

/**
 * Axios构造函数
 * Create a new instance of Axios
 * @param {Object} instanceConfig The default config for the instance
 */
function Axios(instanceConfig) {
    
    
  // 将指定的config, 保存为defaults属性
  this.defaults = instanceConfig;
  // 将包含请求/响应拦截器管理器的对象保存为interceptors属性
  this.interceptors = {
    
    
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

/**
 * 用于发请求的函数
 * 我们使用的axios就是此函数bind()返回的函数
 * 
 * Dispatch a request
 *
 * @param {Object} config The config specific for this request (merged with this.defaults)
 */
Axios.prototype.request = function request(config) {
    
    
  /*eslint no-param-reassign:0*/
  // Allow for axios('example/url'[, config]) a la fetch API
  if (typeof config === 'string') {
    
    
    config = arguments[1] || {
    
    };
    config.url = arguments[0];
  } else {
    
    
    config = config || {
    
    };
  }

  // 合并配置
  config = mergeConfig(this.defaults, config);
  // 添加method配置, 默认为get
  config.method = config.method ? config.method.toLowerCase() : 'get';

  /*
  创建用于保存请求/响应拦截函数的数组
  数组的中间放发送请求的函数
  数组的左边放请求拦截器函数(成功/失败)
  数组的右边放响应拦截器函数
  */
  var chain = [dispatchRequest, undefined];
  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());
  }

  // 返回用来指定我们的onResolved和onRejected的promise
  return promise;
};

// 用来得到带query参数的url
Axios.prototype.getUri = function getUri(config) {
    
    
  config = mergeConfig(this.defaults, config);
  return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, '');
};

// Provide aliases for supported request methods
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
    
    
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, config) {
    
    
    return this.request(utils.merge(config || {
    
    }, {
    
    
      method: method,
      url: url
    }));
  };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
    
    
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    
    
    return this.request(utils.merge(config || {
    
    }, {
    
    
      method: method,
      url: url,
      data: data
    }));
  };
});

module.exports = Axios;

  1. axios 运行的整体流程,如下所示:
  • 整体流程: request(config) ===> dispatchRequest(config) ===> xhrAdapter(config)
  • request(config): 将请求拦截器 / dispatchRequest() / 响应拦截器 通过 promise 链串连起来, 返回 promise
  • dispatchRequest(config): 转换请求数据 ===> 调用 xhrAdapter() 发请求 ===> 请求返回后转换响应数据. 返回 promise
  • xhrAdapter(config) : 创建 XHR 对象, 根据 config 进行相应设置, 发送特定请求, 并接收响应数据, 返回 promise
  1. 对于 axios 的流程,也可以这么理解:axiosaxios.create() 一起发送请求,被 createInstance() 一起接收,配合 config 执行/别名执行,Axios.prototype.request 执行,然后执行 request.interceptors,配合处理参数与默认参数/transformdata 执行 dispatchRequest,然后执行 adapter。如果报错或者是 cancel 取消,那么执行 axios.rejected。如果正确,那么执行 axios fulfilledresponse interceptors 执行,最后请求的 onResolved 或者是 onRejected

  2. 对于 request 的流程,如下所示:

    requestInterceptors: [{fulfilled1(){}, rejected1(){}}, {fulfilled2(){}, rejected2(){}}]
        responseInterceptors: [{fulfilled11(){}, rejected11(){}}, {fulfilled22(){}, rejected22(){}}]
        chain: [
          fulfilled2, rejected2, fulfilled1, rejected1, 
          dispatchReqeust, undefined, 
          fulfilled11, rejected11, fulfilled22, rejected22
        ]
        promise链回调: config 
                      => (fulfilled2, rejected2) => (fulfilled1, rejected1)   // 请求拦截器处理
                      => (dispatchReqeust, undefined) // 发请求
                      => (fulfilled11, rejected11) => (fulfilled22, rejected22) // 响应拦截器处理
                      => (onResolved, onRejected) // axios发请求回调处理
    
  3. 对于 dispatchRequest 的流程,看下源码如下所示:

  • dispatchRequest.js :

     'use strict';
    
     var utils = require('./../utils');
     var transformData = require('./transformData');
     var isCancel = require('../cancel/isCancel');
     var defaults = require('../defaults');
     var isAbsoluteURL = require('./../helpers/isAbsoluteURL');
     var combineURLs = require('./../helpers/combineURLs');
     
     /**
      * Throws a `Cancel` if cancellation has been requested.
      */
     function throwIfCancellationRequested(config) {
          
          
       if (config.cancelToken) {
          
          
         config.cancelToken.throwIfRequested();
       }
     }
     
     /**
      * Dispatch a request to the server using the configured adapter.
      *
      * @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);
     
       /* 
       合并config中的baseURL和url
       */
       if (config.baseURL && !isAbsoluteURL(config.url)) {
          
          
         config.url = combineURLs(config.baseURL, config.url);
       }
     
       // Ensure headers exist
       config.headers = config.headers || {
          
          };
     
       /* 
       对config中的data进行必要的转换处理
       设置相应的Content-Type请求头
       */
       config.data = transformData(
         config.data,
         config.headers,
         config.transformRequest
       );
     
       /* 
       整合config中所有的header
       */
       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);
     
         /* 
         对response中还没有解析的data数据进行解析
         json字符串解析为js对象/数组
         */
         response.data = transformData(
           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(
               reason.response.data,
               reason.response.headers,
               config.transformResponse
             );
           }
         }
     
         return Promise.reject(reason);
       });
     };
    
     ```
    - **InterceptorManager.js** :
     ```js
     'use strict';
    
     var utils = require('./../utils');
     
     function InterceptorManager() {
          
          
       // 用来保存拦截器函数的数组, 数组中每个都是对象, 对象中包含fulfilled/rejected方法
       this.handlers = [];
     }
     
     /**
      * Add a new interceptor to the stack
      *
      * @param {Function} fulfilled The function to handle `then` for a `Promise`
      * @param {Function} rejected The function to handle `reject` for a `Promise`
      *
      * @return {Number} An ID used to remove interceptor later
      */
     InterceptorManager.prototype.use = function use(fulfilled, rejected) {
          
          
       // 添加成功和失败的拦截器函数
       this.handlers.push({
          
          
         fulfilled: fulfilled,
         rejected: rejected
       });
       // 返回拦截器对应的ID(也就是下标)
       return this.handlers.length - 1;
     };
     
     /**
      * Remove an interceptor from the stack
      *
      * @param {Number} id The ID that was returned by `use`
      */
     InterceptorManager.prototype.eject = function eject(id) {
          
          
       // 移除指定id对应的拦截器
       if (this.handlers[id]) {
          
          
         this.handlers[id] = null;
       }
     };
     
     /**
      * Iterate over all the registered interceptors
      *
      * This method is particularly useful for skipping over any
      * interceptors that may have become `null` calling `eject`.
      *
      * @param {Function} fn The function to call for each interceptor
      */
     InterceptorManager.prototype.forEach = function forEach(fn) {
          
          
       // 遍历处理所有保存的拦截器
       utils.forEach(this.handlers, function forEachHandler(h) {
          
          
         if (h !== null) {
          
          
           fn(h);
         }
       });
     };
     
     module.exports = InterceptorManager;
    
     ```
    - **defaults.js** :
     ```js
     'use strict';
    
     var utils = require('./utils');
     var normalizeHeaderName = require('./helpers/normalizeHeaderName');
     
     // 默认的Content-Type头的值
     var DEFAULT_CONTENT_TYPE = {
          
          
       'Content-Type': 'application/x-www-form-urlencoded'
     };
     
     function setContentTypeIfUnset(headers, value) {
          
          
       if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {
          
          
         headers['Content-Type'] = value;
       }
     }
     
     function getDefaultAdapter() {
          
          
       var adapter;
       // Only Node.JS has a process variable that is of [[Class]] process
       if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
          
          
         // For node use HTTP adapter
         adapter = require('./adapters/http');
       } else if (typeof XMLHttpRequest !== 'undefined') {
          
          
         // For browsers use XHR adapter
         adapter = require('./adapters/xhr');
       }
       return adapter;
     }
     
     var defaults = {
          
          
       // 得到当前环境对应的请求适配器
       adapter: getDefaultAdapter(),
     
       // 请求转换器
       transformRequest: [function transformRequest(data, headers) {
          
          
         // 指定headers中更规范的请求头属性名
         normalizeHeaderName(headers, 'Accept');
         normalizeHeaderName(headers, 'Content-Type');
     
     
         if (utils.isFormData(data) ||
           utils.isArrayBuffer(data) ||
           utils.isBuffer(data) ||
           utils.isStream(data) ||
           utils.isFile(data) ||
           utils.isBlob(data)
         ) {
          
          
           return data;
         }
         if (utils.isArrayBufferView(data)) {
          
          
           return data.buffer;
         }
         if (utils.isURLSearchParams(data)) {
          
          
           setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
           return data.toString();
         }
         // 如果data是对象, 指定请求体参数格式为json, 并将参数数据对象转换为json
         if (utils.isObject(data)) {
          
          
           setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
           return JSON.stringify(data);
         }
         return data;
       }],
     
       // 响应数据转换器: 解析字符串类型的data数据
       transformResponse: [function transformResponse(data) {
          
          
         /*eslint no-param-reassign:0*/
         if (typeof data === 'string') {
          
          
           try {
          
          
             data = JSON.parse(data);
           } catch (e) {
          
           /* Ignore */ }
         }
         return data;
       }],
     
       /**
        * A timeout in milliseconds to abort a request. If set to 0 (default) a
        * timeout is not created.
        */
       timeout: 0,
     
       xsrfCookieName: 'XSRF-TOKEN',
       xsrfHeaderName: 'X-XSRF-TOKEN',
     
       maxContentLength: -1,
     
       // 判断响应状态码的合法性: [200, 299]
       validateStatus: function validateStatus(status) {
          
          
         return status >= 200 && status < 300;
       }
     };
     
     defaults.headers = {
          
          
       // 包含所有通用的请求的对象
       common: {
          
          
         'Accept': 'application/json, text/plain, */*'
       }
     };
     
     // 指定delete/get/head请求方式的请求头容器对象
     utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
          
          
       defaults.headers[method] = {
          
          };
     });
     
     // 指定post/put/patch请求方式的请求头容器对象
     utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
          
          
       // 指定了默认的Content-Type头
       defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
     });
     
     module.exports = defaults;
    
     ```
    
    
  1. axios 的请求/响应拦截器是什么? 如下所示:
  • 请求拦截器: 在真正发请求前,可以对请求进行检查或配置进行特定处理的函数, 包括成功/失败的函数, 传递的必须是 config。失败的回调函数, 传递的默认是 error
  • 响应拦截器: 在请求返回后,可以对响应数据进行特定处理的函数,包括成功/失败的函数,传递的默认是 response。失败的回调函数, 传递的默认是 error
  1. axios 的请求/响应数据转换器是什么? 如下所示:
  • 请求转换器:对请求头和请求体数据进行特定处理的函数
    setContentTypeIfUnset(headers, 'application/json;charset=utf-8'); return JSON.stringify(data),如下:

    if (utils.isObject(data)) {
          
          
        setContentTypeIfUnset(headers, 'application/json;charset=utf-8'); 
        return JSON.stringify(data); 
    }
    
  • 响应转换器: 将响应体 json 字符串解析为 js 对象或数组的函数
    response.data = JSON.parse(response.data)

  1. 对于 xhrAdapter 的流程,看下 xhr.js 的源码,如下所示:
'use strict';

var utils = require('./../utils');
var settle = require('./../core/settle');
var buildURL = require('./../helpers/buildURL');
var parseHeaders = require('./../helpers/parseHeaders');
var isURLSameOrigin = require('./../helpers/isURLSameOrigin');
var createError = require('../core/createError');

module.exports = function xhrAdapter(config) {
    
    
  // 返回一个promise
  return new Promise(function dispatchXhrRequest(resolve, reject) {
    
    
    
    var requestData = config.data;
    var requestHeaders = config.headers;

    if (utils.isFormData(requestData)) {
    
    
      delete requestHeaders['Content-Type']; // Let the browser set it
    }

    // 创建XHR对象
    var request = new XMLHttpRequest();

    // HTTP basic authentication
    if (config.auth) {
    
    
      var username = config.auth.username || '';
      var password = config.auth.password || '';
      requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
    }

    // 初始化请求
    request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);

    // 指定超时时间(单位ms)
    request.timeout = config.timeout;

    // 绑定请求状态改变的监听
    request.onreadystatechange = function handleLoad() {
    
    
      // request不存在或请求状态不是4, 直接结束
      if (!request || request.readyState !== 4) {
    
    
        return;
      }

      // The request errored out and we didn't get a response, this will be
      // handled by onerror instead
      // With one exception: request that using file: protocol, most browsers
      // will return status as 0 even though it's a successful request
      if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
    
    
        return;
      }

      // 准备response对象
      var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
      var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
      var response = {
    
    
        data: responseData,
        status: request.status,
        statusText: request.statusText,
        headers: responseHeaders,
        config: config,
        request: request
      };

      // 根据响应状态码来确定请求的promise的结果状态(成功/失败)
      settle(resolve, reject, response);

      // 将请求对象赋空
      request = null;
    };

    // 绑定请求中断监听
    request.onabort = function handleAbort() {
    
    
      if (!request) {
    
    
        return;
      }
      // reject promise, 指定aborted的error
      reject(createError('Request aborted', config, 'ECONNABORTED', request));

      // Clean up request
      request = null;
    };

    // Handle low level network errors
    request.onerror = function handleError() {
    
    
      // Real errors are hidden from us by the browser
      // onerror should only fire if it's a network error
      reject(createError('Network Error', config, null, request));

      // Clean up request
      request = null;
    };

    // Handle timeout
    request.ontimeout = function handleTimeout() {
    
    
      reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED',
        request));

      // Clean up request
      request = null;
    };

    // Add xsrf header
    // This is only done if running in a standard browser environment.
    // Specifically not if we're in a web worker, or react-native.
    if (utils.isStandardBrowserEnv()) {
    
    
      var cookies = require('./../helpers/cookies');

      // Add xsrf header
      var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ?
        cookies.read(config.xsrfCookieName) :
        undefined;

      if (xsrfValue) {
    
    
        requestHeaders[config.xsrfHeaderName] = xsrfValue;
      }
    }

    // 如果没有指定请求体参数, 删除Content-Type请求头, 其它所有请求头都设置到request上
    // Add headers to the request
    if ('setRequestHeader' in request) {
    
    
      utils.forEach(requestHeaders, function setRequestHeader(val, key) {
    
    
        if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
    
    
          // Remove Content-Type if data is undefined
          delete requestHeaders[key];
        } else {
    
    
          // Otherwise add header to the request
          request.setRequestHeader(key, val);
        }
      });
    }

    // Add withCredentials to request if needed
    if (config.withCredentials) {
    
    
      request.withCredentials = true;
    }

    // 如果需要指定responseType
    // Add responseType to request if needed
    if (config.responseType) {
    
    
      try {
    
    
        request.responseType = config.responseType;
      } catch (e) {
    
    
        // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.
        // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.
        if (config.responseType !== 'json') {
    
    
          throw e;
        }
      }
    }

    // 绑定下载进度的监听
    // Handle progress if needed
    if (typeof config.onDownloadProgress === 'function') {
    
    
      request.addEventListener('progress', config.onDownloadProgress);
    }

    // 绑定上传进度的监听
    // Not all browsers support upload events
    if (typeof config.onUploadProgress === 'function' && request.upload) {
    
    
      request.upload.addEventListener('progress', config.onUploadProgress);
    }

    // 如果配置了cancelToken
    if (config.cancelToken) {
    
    
      // 指定用于中断请求的回调函数
      config.cancelToken.promise.then(function onCanceled(cancel) {
    
    
        if (!request) {
    
    
          return;
        }
        // 中断请求
        request.abort();
        // 让请求的promise失败
        reject(cancel);
        // Clean up request
        request = null;
      });
    }

    if (requestData === undefined) {
    
    
      requestData = null;
    }

    // 发送请求, 指定请求体数据, 可能是null
    request.send(requestData);
  });
};

  1. response 的整体结构,如下所示:

        {
            data,
            status,
            statusText,
            headers,
            config,
            request
        }
    
  2. error 的整体结构,如下所示:

        {
            message,
            request,
            response
        }
    
  3. 对于 cancel 取消的流程,看下源码如下所示:

    扫描二维码关注公众号,回复: 12826798 查看本文章
    • Cancel.js :
      'use strict';
      
      /**
       * 当取消一个请求时, 需要将Cancel对象作为一个error抛出
       * 
       * A `Cancel` is an object that is thrown when an operation is canceled.
       *
       * @class
       * @param {string=} message The message.
       */
      function Cancel(message) {
              
              
        this.message = message;
      }
      
      Cancel.prototype.toString = function toString() {
              
              
        return 'Cancel' + (this.message ? ': ' + this.message : '');
      };
      
      // 用于标识是一个取消的error
      Cancel.prototype.__CANCEL__ = true;
      
      module.exports = Cancel;
      
      
    • isCancel.js :
      'use strict';
      /* 
      用于判断一个error是不是一个cancel错误
      */
      module.exports = function isCancel(value) {
              
              
        return !!(value && value.__CANCEL__);
      };
      
      
    • CancelToken.js :
      'use strict';
      
      var Cancel = require('./Cancel');
      
      /**
       * 用于取消请求的对象构造函数
       * 
       * A `CancelToken` is an object that can be used to request cancellation of an operation.
       *
       * @class
       * @param {Function} executor The executor function.
       */
      function CancelToken(executor) {
              
              
        if (typeof executor !== 'function') {
              
              
          throw new TypeError('executor must be a function.');
        }
      
        // 为取消请求准备一个promise对象, 并保存resolve函数
        var resolvePromise;
        this.promise = new Promise(function promiseExecutor(resolve) {
              
              
          resolvePromise = resolve;
        });
      
        // 保存当前token对象
        var token = this;
      
        // 立即执行接收的执行器函数, 并传入用于取消请求的cancel函数
        executor(function cancel(message) {
              
              
          // 如果token中有reason了, 说明请求已取消
          if (token.reason) {
              
              
            // Cancellation has already been requested
            return;
          }
          // 将token的reason指定为一个Cancel对象
          token.reason = new Cancel(message);
          // 将取消请求的promise指定为成功, 值为reason
          resolvePromise(token.reason);
        });
      }
      
      /**
       * 如果请求已经被取消, 抛出reason也就是Cancel对象的异常
       * Throws a `Cancel` if cancellation has been requested.
       */
      CancelToken.prototype.throwIfRequested = function throwIfRequested() {
              
              
        if (this.reason) {
              
              
          throw this.reason;
        }
      };
      
      /**
       * 创建一个包含token对象和cancel函数的对象, 并添加给CancelToken
       * 
       * Returns an object that contains a new `CancelToken` and a function that, when called,
       * cancels the `CancelToken`.
       */
      CancelToken.source = function source() {
              
              
        var cancel;
        var token = new CancelToken(function executor(c) {
              
              
          cancel = c;
        });
        return {
              
              
          token: token,
          cancel: cancel
        };
      };
      
      module.exports = CancelToken;
      
      
  4. 如何取消未完成的请求,如下所示:

  • 当配置了 cancelToken 对象时,保存 cancel 函数,如下:
    • 创建一个用于将来中断请求的 cancelPromise
    • 并定义了一个用于取消请求的 cancel 函数
    • cancel 函数传递出来
  • 调用 cancel() 取消请求,如下:
    • 执行 cacel 函数, 传入错误信息 message
    • 内部会让 cancelPromise 变为成功, 且成功的值为一个 Cancel 对象
    • cancelPromise 的成功回调中中断请求, 并让发请求的 proimse 失败, 失败的 reasonCacel 对象

猜你喜欢

转载自blog.csdn.net/weixin_42614080/article/details/107031241