1. 程式人生 > 其它 >Axios取消請求以及其原理(v0.26.1)

Axios取消請求以及其原理(v0.26.1)

Axios取消請求以及其原理(v0.26.1)

1. 取消請求

const axios = require('axios')

const instance = new axios.Axios({})
// 建立source,通過source.cancel()取消請求
const source = new axios.CancelToken.source()

instance.defaults.timeout = 10000

instance.interceptors.request.use(
  config => {
    config.cancelToken = source.token
    return config
  }
)

// 此處寫了兩個響應攔截器是測試用,
// 因為我在專案中看到他人
// 定義了一個全域性的CancelToken.source。當請求失敗
// 報錯後執行source(),使config.token.reason.message = undefined
// dispatchRequest時會呼叫CancelToken.prototype.throwIfRequested方法,
// 直接丟擲異常資訊config.token.reason,不傳送ajax請求
instance.interceptors.response.use(
  ({ data, status, statusText }) => {
    if (status < 200 || status > 399) {
      throw statusText
    }
    return data
  }
)
instance.interceptors.response.use(
  undefined,
  error => {
    // 取消請求
    source.cancel()
    throw error
  }
)

2. 取消請求的原理

呼叫 CancelToken.source.cancel方法 時,將 CancelToken例項 上的 promise 屬性的狀態改為 fulfilled

CancelToken.source方法

CancelToken.source = function source() {
  var cancel;
  // 用cancel儲存executor中的方法c方法,執行cancel()時,會將token.promise的狀態變為fulfilled
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  return {
    token: token,
    cancel: cancel
  };
};

CancelToken建構函式

function CancelToken(executor) {
  // 此處省略executor須為function型別的校驗

  // 用resolvePromise儲存Promise中的resolve方法,在執行source.cancel()的時候呼叫
  var resolvePromise;
  this.promise = new Promise((resolve) => {
    resolvePromise = resolve;
  });

  var token = this;

  // promise的狀態變為fulfilled時,遍歷_listeners並執行裡面的回撥
  this.promise.then(function(cancel) {
    if (!token._listeners) return;
    var i;
    var l = token._listeners.length;
    for (i = 0; i < l; i++) {
      token._listeners[i](cancel);
    }
    token._listeners = null;
  });

  
  // 重寫promise.then方法
  // 若已取消請求,會在token.subscribe方法中執行resolve(this.reason)
  this.promise.then = function(onfulfilled) {
    var _resolve;
    // eslint-disable-next-line func-names
    var promise = new Promise(function(resolve) {
      token.subscribe(resolve);
      _resolve = resolve;
    }).then(onfulfilled);

    // 取消訂閱
    promise.cancel = function reject() {
      token.unsubscribe(_resolve);
    };

    return promise;
  };

  executor(function cancel(message) {
    if (token.reason) {
      // Cancellation has already been requested
      return;
    }

    token.reason = new CanceledError(message);
    // 改變token.promise的狀態為fulfilled
    resolvePromise(token.reason);
  });
}

CancelToken.prototype.subscribe方法

/**
 *  利用釋出訂閱者模式,收集回撥(執行xhr.abort方法)
 */
CancelToken.prototype.subscribe = function subscribe(listener) {
  // 如果有reason,說明請求已取消,立即執行listener
  if (this.reason) {
    listener(this.reason);
    return;
  }

  if (this._listeners) {
    this._listeners.push(listener);
  } else {
    this._listeners = [listener];
  }
};

明白以上邏輯後,剩下要做的就是訂閱 xhr.abort方法