一比一還原axios原始碼(三)—— 錯誤處理

Zaking發表於2022-03-16

  前面的章節我們已經可以正確的處理正確的請求,並且通過處理header、body,以及加入了promise,讓我們的程式碼更像axios了。這一章我們一起來處理ajax請求中的錯誤。

一、錯誤處理

  首先我們要知道錯誤有哪些型別,通常我們遇到的錯誤有以下幾種:網路錯誤、超時錯誤和非200狀態碼錯誤。其實都不復雜我們來看下:

1、網路異常

  網路異常,會觸發XMLHttpRequest的onerror事件,所以我們只需要加上就可以了:

request.onerror = function handleError() {
  reject(new Error("Network Error"));
};

2、超時錯誤

  XMLHttpRequest物件允許配置timeout引數,預設是0,也就是永遠不會超時,所以我們的程式碼這樣處理就可以了,注意,這裡的config.timeout的config,實際上就是我們傳入的配置引數,只不過這裡引用了一下,包括後續的完整的實現其他api的部分,有很多其實都是對原生api的一個簡單的對映和轉換,後面再說:

if (config.timeout) {
  request.timeout = config.timeout;
}

request.ontimeout = function handleTimeout() {
  reject(new Error(`Timeout of ${config.timeout} ms exceeded`));
};

3、非200狀態碼

  如果請求報錯。那麼XMLHttpRequest的status屬性會返回0,所以我們需要額外判斷下status,中斷後續的程式碼:

   我們在onreadystatechange回撥中加入status的判斷。然後我們改變一下丟擲response的方式,使用一個函式來處理:

return new Promise((resolve, reject) => {
var request = new XMLHttpRequest();
config.headers = processHeaders(config.headers || {}, config.data);
config.data = transformRequest(config.data);
request.open(
  config.method.toUpperCase(),
  buildURL(config.url, config.params, config.paramsSerializer),
  true
);
request.onreadystatechange = function handleLoad() {
  if (request.readyState !== 4) {
    return;
  }
  if (request.status === 0) {
    return;
  }
  const responseHeaders = parseHeaders(request.getAllResponseHeaders());
  const responseData =
    config.responseType && config.responseType !== "text"
      ? request.response
      : request.responseText;
  const response = {
    data: responseData,
    status: request.status,
    statusText: request.statusText,
    headers: responseHeaders,
    config,
    request,
  };
  handleResponse(response);
};
// 加了這個方法來集中處理response
function handleResponse(response) { if (response.status >= 200 && response.status < 300) { resolve(response); } else { reject(new Error(`Request failed with status code ${response.status}`)); } }

  很簡單~~。錯誤處理完成到這裡實際上就完成了,簡單總結下,攔截了readystatechange事件中的status並根據對應的情況,處理response是resolve還是reject。然後根據timeout和error事件來丟擲對應的錯誤。

  但是到這裡還沒真正的完成錯誤的處理,因為我們在錯誤處理的時候僅僅丟擲了錯誤資訊,沒辦法處理一些額外的資料,比如請求配置、響應物件等。我們下面來完成一個Error類,集中處理這些情況,讓我們的程式碼更健壯。

二、完成AxiosError

  首先我們在core資料夾下建立一個createError檔案:

export default function createError(message, config, code, request, response) {
  var error = new Error(message);
  return enhanceError(error, config, code, request, response);
}

  我們來看上面的程式碼,整個createError方法,返回了報錯資訊、配置、狀態碼、請求和響應內容。然後我們實際執行返回的是enhanceError方法。我們再來在core資料夾下建立一個enhanceError檔案:

export default function enhanceError(error, config, code, request, response) {
  error.config = config;
  if (code) {
    error.code = code;
  }

  error.request = request;
  error.response = response;
  error.isAxiosError = true;

  error.toJSON = function toJSON() {
    return {
      // Standard
      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,
    };
  };
  return error;
}

  上面的程式碼,很簡單,把所有的資訊繫結到error物件上,最後再返回error即可。其中需要注意的是,error.toJSON這個東西,它實際上做的就是當你在外層呼叫error的toJSON方法的時候,會返回這個更改後的方法。相當於改寫了這個物件上的toJSON方法。

  比如我們列印下這個東西:

console.log(
  createError(timeoutErrorMessage, config, "ETIMEDOUT", request).toJSON()
);

  就是enhanceError返回的那個。OK,到此我們已經寫好了createError方法(其實我是從原始碼複製過來的,一點修改都沒有)。那麼我們需要修改下之前錯誤處理中的程式碼,至於具體修改的方法,就當留個作業了。大家也可以去專案中的c3分支檢視。

  到此,我們處理完了錯誤資訊,新增了新的createError方法。到目前為止,其實程式碼都還不是真正的axios,為什麼這麼說呢,到現在,我們只是實現了其中的功能,但是其實還不是真正的axios原始碼的組織方式,我們下一章,就來擴充套件整個zaking-axios,修改檔案的相關性,建立Axios類等,來完成更多的功能。

 

相關文章