axios调用办法

  1. axios.create(config)
  2. axios.method(url, config)

完成

function createInstance(defaultConfig) {
  const context = new Axios(defaultConfig)
  const instance = Axios.prototype.request.bind(context)
  // 完成调用办法二
  // 将Axios.prototype办法都仿制给instance,context作为this
  utils.extend(instance, Axios.prototype, context)
  // 将context的一切实例特点也仿制给instance
  utils.extend(instance, context, null)
  // 完成调用办法一
  instance.create = function create(config) {
    return createInstance(mergeConfig(default, config))
  }
  return instance
}
const axios = createInstance(defaultConfig)

defaults中有什么

const defaults = {
  ...,
  headers: {
    common: {
      'Accept': 'application/json, text/plain, */*',
      'Content-Type': undefined
    }
  }
}
utils.forEach(['delete', 'get', 'head', 'post', 'put', 'patch'], (method) => {
  defaults.headers[method] = {};
});
  • 由此可见,defaults会为headers中增加common以及其他恳求办法为特点名,值为对象

Axios类

class Axios {
  constructor(instanceConfig) {
    this.defaults = instanceConfig
    this.interceptors = {
      request: new InterceptorManager()
      response: new InterceptorManager(),
    }
  }
  request(configOrUrl, config) {
    if (typeof configOrUrl === 'string') {
      config = config || {}
      config.url = configOrUrl
    } else {
      config = configOrUrl || {}
    }
    config = mergeConfig(this.defaults, config)
    config.method = (config.method || thhis.default.method || 'get').toLowerCase()
    const { headers } = config;
    let contextHeaders = headers && utils.merge(
      headers.common,
      headers[config.method]
    );
    // 删除冗余
    headers && utils.forEach(
      ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
      (method) => {
        delete headers[method];
      }
    );
    config.headers = AxiosHeaders.concat(contextHeaders, headers);
    const requestInterceptorChain = [];
    this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
      ...
      requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
    });
    const responseInterceptorChain = [];
    this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
      responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
    });
    const chain = [dispatchRequest.bind(this), undefined];
    chain.unshift.apply(chain, requestInterceptorChain);
    chain.push.apply(chain, responseInterceptorChain);
    len = chain.length;
    promise = Promise.resolve(config);
    while (i < len) {
      promise = promise.then(chain[i++], chain[i++]);
    }
    return promise;
  }
}
  • 这当地主要是处理了config、URL以及header,然后将恳求拦截器逆序参加数组,呼应拦截器次序参加数组,当一个恳求发送出去的时分为 恳求拦截器逆序履行 -> 发送恳求 -> 呼应拦截器次序履行

拦截器

class InterceptorManager {
  constructor() {
    this.handlers = []
  }
  use(fulfilled, rejected, options) {
    this.handlers.push({
      fulfilled,
      rejected,
      synchronous: options ? options.synchronous : false,
      runWhen: options ? options.runWhen : null
    })
    return this.handlers.length - 1
  }
  eject(id) {
    if (this.handlers[id]) {
      this.handlers[id] = null;
    }
  }
  clear() {
    if (this.handlers) {
      this.handlers = [];
    }
  }
}
  • 拦截器办理便是一个数组,使用use寄存成功和失利对应的回调,会回来一个id也便是当时寄存的下标,用于撤销寄存

发送恳求的dispatchRequest

function throwIfCancellationRequested(config) {
  if (config.cancelToken) {
    config.cancelToken.throwIfRequested();
  }
  if (config.signal && config.signal.aborted) {
    throw new CanceledError(null, config);
  }
}
function dispatchRequest(config) {
  throwIfCancellationRequested(config); // 判别是否终止了恳求
  const adapter = adapters.getAdapter(config.adapter || defaults.adapter);
  return adapter(config).then(response => {
    throwIfCancellationRequested(config); // 即便成功了也需求判别在发送恳求是否终止了
    ...
    return response
  }, reason => {
    if (!isCancel(reason)) {
      throwIfCancellationRequested(config);
    }
    return Promise.reject(reason)
  })
}

真正发送恳求的adapter

  • Axios 是一个基于promise 网络恳求库,作用于node和阅读器中。 在服务端它使用原生 node.jshttp模块, 而在客户端 (阅读端) 则使用 XMLHttpRequests
const knownAdapters = {
  http: httpAdapter,
  xhr: xhrAdapter
}
// xhrAdapter
function dispatchXhrRequest(config) {
  return new Promise((resolve, reject) => {
    let request = new XMLHttpRequest();
    const fullPath = buildFullPath(config.baseURL, config.url);
    request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
    request.timeout = config.timeout;
    request.onabort = function handleAbort() {
      if (!request) {
        return;
      }
      reject(new AxiosError('Request aborted', AxiosError.ECONNABORTED, config, request));
      // Clean up request
      request = null;
    };
    request.onerror = function handleError() {
      reject(new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request));
      // Clean up request
      request = null;
    };
    request.ontimeout = function handleTimeout() {
      let timeoutErrorMessage = config.timeout ? 'timeout of ' + config.timeout + 'ms exceeded' : 'timeout exceeded';
      const transitional = config.transitional || transitionalDefaults;
      if (config.timeoutErrorMessage) {
        timeoutErrorMessage = config.timeoutErrorMessage;
      }
      reject(new AxiosError(
        timeoutErrorMessage,
        transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,
        config,
        request));
      // Clean up request
      request = null;
    };
    let onCanceled;
    if (config.cancelToken || config.signal) {
      onCanceled = cancel => {
        if (!request) {
          return;
        }
        reject(!cancel || cancel.type ? new CanceledError(null, config, request) : cancel);
        request.abort();
        request = null;
      };
      config.cancelToken && config.cancelToken.subscribe(onCanceled); // 调用cancelToken.cancel的时分会去调用订阅的办法,然后能够撤销promise
      if (config.signal) {
        config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);
      }
    }
    function onloadend() {
      if (!request) {
        return;
      }
      // Prepare the response
      const responseHeaders = AxiosHeaders.from(
        'getAllResponseHeaders' in request && request.getAllResponseHeaders()
      );
      const responseData = !responseType || responseType === 'text' || responseType === 'json' ?
        request.responseText : request.response;
      const response = {
        data: responseData,
        status: request.status,
        statusText: request.statusText,
        headers: responseHeaders,
        config,
        request
      };
      settle(function _resolve(value) {
        resolve(value);
        done();
      }, function _reject(err) {
        reject(err);
        done();
      }, response);
      // Clean up request
      request = null;
    }
    function done() { // 履行一些铲除操作
      if (config.cancelToken) {
        config.cancelToken.unsubscribe(onCanceled);
      }
      if (config.signal) {
        config.signal.removeEventListener('abort', onCanceled);
      }
    }
    request.onloadend = onloadend;
    request.send(requestData || null);
  })
}
  • 总的来说便是根据环境判别能够用node的http还是阅读器的xhr,其间对config传入的cancelToken调用其订阅办法,订阅能够撤销promise的办法,当撤销的时分会调用request.abort()然后reject掉promise,即便发送了网络恳求,在后续adapter根据promise对应的resolve或reject状况会进行判别是否撤销过,撤销过则丢出过错。

撤销恳求的CancelToken

class CancelToken {
  static source() {
    let cancel
    const token = new CancelToken(c => {
      cancel = c
    })
    return {
      token,
      cancel
    }
  }
  constructor(executor) {
    const token = this
    let resolvePromise;
    executor(function cancel(message, config, request) {
      if (token.reason) { // 存在reason阐明现已调用过
        return
      }
      token.reason = new CanceledError(message, config, request);
      resolvePromise(token.reason)
    })
    token.promise = new Promise((resolve, reject) => {
      resolvePromise = resolve
    })
    this.promise.then(cancel => {
      if (!token._listeners) return;
      let i = token._listeners.length;
      while (i-- > 0) {
        token._listeners[i](cancel); // 履行一切订阅的函数,其间包含在adapter中存储的cancel
      }
      token._listeners = null;
    });
  }
  subscribe(listener) {
    if (this.reason) {
      listener(this.reason);
      return;
    }
    if (this._listeners) {
      this._listeners.push(listener);
    } else {
      this._listeners = [listener];
    }
  }
}
  • 总而言之,撤销函数是通过向外传递一个变量cancel,该变量又被赋值为一个函数,该函数被调用就会给当时this(也便是token)寄存一个reason标志现已调用过,履行promise的resolve办法,然后履行一切订阅的函数,包含了adapter中存储的cancel(也便是会履行request.abort导致request地点的promise状况变为reject)