zoukankan      html  css  js  c++  java
  • axios源码学习记录

    带着问题读源码,主要解决以下几个问题
    1、为什么 axios 既可以当函数调用,也可以当对象使用
    2、 axios 调用流程及原理
    3、有用过拦截器吗?原理是怎样的?
    4、axios取消请求及原理。
    5、为什么支持浏览器中发送请求也支持node发送请求?
    一、为什么 axios 既可以当函数调用,也可以当对象使用
    axios调用了Axios函数,Axios函数的原型上绑定了很多方法。本质上两种方法最终都是调用了Axios。prototype.request同一个函数
    // 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
        }));
      };
    });

    二、axios的调用流程

    流程中最关键的的有三点

    1、promise链式调用

    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);
      });
    
      while (chain.length) {
        promise = promise.then(chain.shift(), chain.shift());
      }
    promise链式调用最明显的就是这几行代码。
    首先新建一个chain数组,一个为dispatchRequest方法,一个是为了对称加入的undefined
    然后通过拦截器的forEach依次在头部加入请求拦截器的成功和失败函数。
    再依次在数组尾部加入响应拦截器的成功和失败还是。
    最后通过while函数循环从首部依次执行,请求拦截器=》请求=》响应拦截器,形成一个promise链

    2、dispatchRequest函数

    dispatchRequest函数用来真正发送请求。
    它通过适配器函数adapter来判断是在浏览器环境还是node环境,以便使用xhr或者http来发送请求。
    主要流程是每次执行时先判断是否有取消请求动作,如果有直接取消执行,如果没有接着向下执行
    function dispatchRequest(config) {
      throwIfCancellationRequested(config);
    
      // Support baseURL config
      if (config.baseURL && !isAbsoluteURL(config.url)) {
        config.url = combineURLs(config.baseURL, config.url);
      }
    
      // 确保 config.header 存在。
      config.headers = config.headers || {};
    
      // Transform request data
      config.data = transformData(
        config.data,
        config.headers,
        config.transformRequest
      );
    
      // 拍平 config.header。
      config.headers = utils.merge(
        config.headers.common || {},
        config.headers[config.method] || {},
        config.headers || {}
      );
    
    //删除一些 config.header。
      utils.forEach(
        ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
        function cleanHeaderConfig(method) {
          delete config.headers[method];
        }
      );
    
      var adapter = config.adapter || defaults.adapter;
    //返回适配器adapter(Promise实例)执行后 then执行后的 Promise实例。
    //返回结果传递给响应拦截器处理。
      return adapter(config).then(function onAdapterResolution(response) {
          //判断是否取消请求
        throwIfCancellationRequested(config);
    
        // Transform response data
        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);
      });
    };

    3、取消请求函数

     
    function CancelToken(executor) {
      if (typeof executor !== 'function') {
        throw new TypeError('executor must be a function.');
      }
    
      var resolvePromise;
      this.promise = new Promise(function promiseExecutor(resolve) {
        resolvePromise = resolve;
      });
    
      var token = this;
      executor(function cancel(message) {
        if (token.reason) {
          // Cancellation has already been requested
          return;
        }
    
        token.reason = new Cancel(message);
        resolvePromise(token.reason);
      });
    }
    
    /**
     * Throws a `Cancel` if cancellation has been requested.
     */
    CancelToken.prototype.throwIfRequested = function throwIfRequested() {
      if (this.reason) {
        throw this.reason;
      }
    };
    
    /**
     * 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;
    axios的取消使用方法:

     
    // 第一种取消方法
    axios.get(url, {
      cancelToken: new axios.CancelToken(cancel => {
        if (/* 取消条件 */) {
          cancel('取消请求');
        }
      })
    });
    
    // 第二种取消方法
    const CancelToken = axios.CancelToken;
    const source = CancelToken.source();
    axios.get(url, {
      cancelToken: source.token
    });
    source.cancel('取消请求');
    两种方法都是调用的 new CancelToken里的executor函数
    axios在每次发送请求时都会判断是否已经执行了executor函数,如果在发送请求前已经执行,直接throw error,停止发送请求。
    如果已经执行到request函数,则直接返回reject(reason),这时,xhrpromise链直接执行xhr.abort(),并且返回reject

     
    //xhr.js
    if (config.cancelToken) {
          // Handle cancellation
          config.cancelToken.promise.then(function onCanceled(cancel) {
            if (!request) {
              return;
            }
    
            request.abort();
            reject(cancel);
            // Clean up request
            request = null;
          });
        }
    4、拦截器构造函数
    function InterceptorManager() {
      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
      });
      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) {
      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;

    拦截器构造函数只有三个方法,添加拦截器,注销拦截器,执行所有的拦截器
     
     
     
     
     

  • 相关阅读:
    合肥程序员欢迎进QQ群49313181同城程序员职业交流群
    新一代程序员的思考
    ThinkPHP开发系列一框架搭建
    ASP.NET MVC4+EF系列之五 架构介绍
    ASP.NET MVC4+EF系列之阶段源码一
    gcc g++ Linux下动态库_静态库 指定路径问题
    [转]accept() 产生的Socekt端口是多少?
    阿里云计算资深总监唐洪:飞天大规模分布式计算系统解析
    [转] C++中##(两个井号)和#(一个井号)用法
    deep learning 深度学习
  • 原文地址:https://www.cnblogs.com/qdcnbj/p/12738019.html
Copyright © 2011-2022 走看看