从零实现axios (4.1小节-错误处理)

273 阅读2分钟

错误处理1

我们在 core 文件下创建 AxiosError.js 文件,在文件里实现 AxiosError错误类。AxiosError 类做的事情非常简单,就是对浏览器原生的 Error 类进行继承、扩展。我们直接看代码注释

"use strict";

var utils = require("../utils");

/**
 * 通过指定的消息、错误码等参数创建一个错误实例
 * @param {string} message 错误消息.
 * @param {string} [code] 错误码 (例如: 'ECONNABORTED').
 * @param {Object} [config] axios的配置对象.
 * @param {Object} [request] XMLHttpRequest实例.
 * @param {Object} [response] 请求的响应对象.
 * @returns {Error} The created error.
 */
function AxiosError(message, code, config, request, response) {
  Error.call(this); // 继承Error的属性

  if (Error.captureStackTrace) {
    Error.captureStackTrace(this); // 错误信息的堆栈追踪
  }

  this.message = message;
  this.name = "AxiosError";
  code && (this.code = code);
  config && (this.config = config);
  request && (this.request = request);
  response && (this.response = response);
}

// AxiosError继承Error的原型(Error.prototype), 同时继承toJSON方法
utils.inherits(AxiosError, Error, {
  toJSON: function toJSON() {
    return {
      // 标准浏览器的错误信息
      message: this.message,
      name: this.name,
      // Microsoft的错误信息
      description: this.description,
      number: this.number,
      // Mozilla的错误信息
      fileName: this.fileName,
      lineNumber: this.lineNumber,
      columnNumber: this.columnNumber,
      stack: this.stack,
      // Axios本身的一些信息
      config: this.config,
      code: this.code,
      status:
        this.response && this.response.status ? this.response.status : null,
    };
  },
});

var prototype = AxiosError.prototype;
var descriptors = {};

// 错误码
[
  "ERR_BAD_OPTION_VALUE",
  "ERR_BAD_OPTION",
  "ECONNABORTED",
  "ETIMEDOUT",
  "ERR_NETWORK",
  "ERR_FR_TOO_MANY_REDIRECTS",
  "ERR_DEPRECATED",
  "ERR_BAD_RESPONSE",
  "ERR_BAD_REQUEST",
  "ERR_CANCELED",
  // eslint-disable-next-line func-names
].forEach(function (code) {
  descriptors[code] = { value: code };
});

// 把错误码作为AxiosError的静态属性
// 这样就可以通过AxiosError.ERR_BAD_REQUEST的方式访问错误码
Object.defineProperties(AxiosError, descriptors);
// 表明一个对象是AxiosError实例的标志
Object.defineProperty(prototype, "isAxiosError", { value: true });

// 通过一个错误实例(error)来创建一个axios错误(axiosError), 而不用通过new AxiosError()的方式
AxiosError.from = function (
  error,
  code,
  config,
  request,
  response,
  customProps
) {
  var axiosError = Object.create(prototype);

  // 把error的属性拷贝到axiosError中
  utils.toFlatObject(error, axiosError, function filter(obj) {
    // 自定义的过滤函数,用于决定是否拷贝obj,返回false则不拷贝
    // obj是Error.prototype时,不拷贝obj
    return obj !== Error.prototype;
  });

  // axiosError作为AxiosError的this上下文,等同于new AxiosError()
  AxiosError.call(axiosError, error.message, code, config, request, response);

  axiosError.name = error.name;

  customProps && Object.assign(axiosError, customProps);

  return axiosError;
};

module.exports = AxiosError;

我们在 AxiosError 中用了 utils.inheritsutils.toFlatObject方法,现在我们来实现它们

/**
 * constructor继承superConstructor的原型方法
 * @param {function} constructor 子类
 * @param {function} superConstructor 父类
 * @param {object} [props] 用于扩展子类的原型方法
 * @param {object} [descriptors] 属性描述符,可以往constructor.prototype上添加属性
 */
function inherits(constructor, superConstructor, props, descriptors) {
  constructor.prototype = Object.create(
    superConstructor.prototype,
    descriptors
  );
  constructor.prototype.constructor = constructor;
  props && Object.assign(constructor.prototype, props);
}

/**
 * 把sourceObj上的内容,包括原型链(Object.prototype除外)上的属性、方法拷贝到destObj上
 * @param {Object} sourceObj 源对象
 * @param {Object} [destObj] 目的对象
 * @param {Function} [filter] 自定义的过滤函数,用于决定是否拷贝obj,返回false则不拷贝
 * @returns {Object}
 */
function toFlatObject(sourceObj, destObj, filter) {
  var props;
  var i;
  var prop;
  var merged = {};

  destObj = destObj || {};

  do {
    props = Object.getOwnPropertyNames(sourceObj);
    i = props.length;
    while (i-- > 0) {
      prop = props[i];
      if (!merged[prop]) {
        destObj[prop] = sourceObj[prop];
        merged[prop] = true;
      }
    }
    sourceObj = Object.getPrototypeOf(sourceObj);
  } while (
    sourceObj &&
    (!filter || filter(sourceObj, destObj)) &&
    sourceObj !== Object.prototype
  );

  return destObj;
}