axios: 攔截器的設計與實現

簡介

Axios 是一個基於 Promise 的 HTTP 客戶端,同時支持瀏覽器和 Node.js 環境。它是一個優秀的 HTTP 客戶端,被普遍地應用在大量的 Web 項目中。具體介紹可見官方文檔javascript

對於大多數應用來講,都會遇到要統一處理ajax請求的場景,爲了較好地解決這個問題,攔截器就應運而生了。
在Axios中它提供了請求攔截器和響應攔截器來分別處理請求和響應,它們的做用以下:java

  • 請求攔截器:該類攔截器的做用是在請求發送前統一執行某些操做,好比在請求頭中添加 token 字段。
  • 響應攔截器:該類攔截器的做用是在接收到服務器響應後統一執行某些操做,好比發現響應狀態碼爲 401 時,自動跳轉到登陸頁。

接下來,本文將經過axios源碼來闡述攔截器是如何設計實現的。ios

設計與實現

任務的註冊

首先如下面的代碼爲例,經過use將方法註冊到攔截器中git

// request interceptor
axios.interceptors.request.use(
  config => {
    console.log('config', config);
    return config;
  },
  err => Promise.reject(err),
);

// response interceptor
axios.interceptors.response.use(response => {
  console.log('response', response);
  return response;
});

// axios/lib/core/InterceptorManager.js
// 在攔截器管理類中經過use方法向任務列表中添加任務
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1;
};

任務的編排

// 先看lib/axios.js(入口文件) 中的建立實例的方法
function createInstance(defaultConfig) {
  var context = new Axios(defaultConfig);

  // REVIEW[epic=interceptors,seq=0] 在axios對象上綁定request方法,使得axios({option})這樣的方式便可調用request方法
  var instance = bind(Axios.prototype.request, context);

  // Copy axios.prototype to instance
  utils.extend(instance, Axios.prototype, context);

  // Copy context to instance
  utils.extend(instance, context);

  return instance;
}
// 重點在於Axios.prototype.request
Axios.prototype.request = function request(config) {
    // ...已省略部分代碼
  // Hook up interceptors middleware
  // REVIEW[epic=interceptors,seq=2] dispatchRequest 爲咱們使用axios時,項目中調用的請求
  var chain = [dispatchRequest, undefined];
  var promise = Promise.resolve(config);

  // REVIEW[epic=interceptors,seq=4] 向攔截器任務列表的頭部註冊 request任務
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  // REVIEW[epic=interceptors,seq=5] 向攔截器任務列表的尾部註冊 response任務
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });
  // 經過上面的註冊方式,咱們能夠知道最後的chain數組會長成的樣子是:
  // [ ...requestInterceptor, dispatchRequest,undefined, ...responseInterceptor ]
  // 這樣就保證了攔截器執行的順序

  while (chain.length) {
    // 由於是成對註冊的任務(fulfilled, rejected)因此執行的時候也是shift2次
    promise = promise.then(chain.shift(), chain.shift());
  }
  return promise;
};

能夠看出經過從不一樣的位置向任務列表中添加任務,實現了任務的編排,達到了按requestInterceptor => Request => responseInterceptor 順序執行的目的github

任務的調度

任務的調度主要是看上面 request函數中的這一行ajax

var promise = Promise.resolve(config);
  while (chain.length) {
    // 由於是成對註冊的任務(fulfilled, rejected)因此執行的時候也是shift2次
    promise = promise.then(chain.shift(), chain.shift());
  }

能夠看出就是按註冊的順序依次執行,而且每一個任務中都須要返回config。axios

結語

在此次的源碼閱讀時,明顯感受到,由於以前幾回的積累,讀源碼這件事開始變得沒有那麼的「困難」了。可是在寫文檔的時候如何更清晰地表達,仍是遇到了點問題。所以借鑑了下網上已有的文檔,使用了任務註冊 => 任務編排 => 任務調度,以任務爲視角來作解析的方式來闡述代碼。數組

相關文章
相關標籤/搜索