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;

 

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

posted @ 2020-04-20 15:25  前端++  阅读(395)  评论(0编辑  收藏  举报