Axios CancelToken 源码解析

129 阅读2分钟

Axios 新版本 CancelToken 已被 AbortController 替换

一、Axios CancelToken 应用

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function (thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // handle error
  }
});

axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})

// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');

二、源码解析

  1. 先抛出 cancelToken 内部使用方法:
// lib/adapters/xhr.js 和 lib/adapters/http.js 都有类似代码:
// 取消事件监听
function done() {
  if (config.cancelToken) {
    config.cancelToken.unsubscribe(onCanceled);
  }
}

// 监听中断事件
config.cancelToken && config.cancelToken.subscribe(onCanceled)

请求无论成功失败都会调用 done,可理解为:requestPromise().finally(done)

  1. CancelToken 源码解析注解:
class CancelToken {
  constructor(executor) {
    // 保证入参类型
    if (typeof executor !== 'function') {
      throw new TypeError('executor must be a function.');
    }

    // Promise 异步技巧,将 resolve 赋值给 resolvePromise,resolvePromise 执行则 Promise 状态改变
    let resolvePromise;

    this.promise = new Promise(function promiseExecutor(resolve) {
      resolvePromise = resolve;
    });

    const token = this;

    // resolvePromise 执行时,此处注册的 then 回调执行
    this.promise.then(cancel => {
      if (!token._listeners) return;

      // 注册的事件监听器执行
      let i = token._listeners.length;

      while (i-- > 0) {
        token._listeners[i](cancel);
      }
      token._listeners = null;
    });

    // 重写了 this.promise.then 方法,支持:`token.promise.then(onFulfilled)`
    this.promise.then = onfulfilled => {
      // 定义 _resolve 方便 subscribe 和 unsubscribe 使用
      let _resolve;
      const promise = new Promise(resolve => {
        // resolve 作为 listener 被传入,监听中断事件
        token.subscribe(resolve);
        _resolve = resolve;
      })
      // 中断事件发生后执行 onfullfilled()
      .then(onfulfilled);

      // 支持取消监听
      promise.cancel = function reject() {
        token.unsubscribe(_resolve);
      };

      return promise;
    };

    // 通过 executor 将内部逻辑作为回调暴露给外部调用者,外部执行 cancel() 则推动此处逻辑进行
    // 可结合类方法 source() 来理解
    executor(function cancel(message, config, request) {
      // 用户重复调用 cancel()
      if (token.reason) {
        return;
      }

      token.reason = new CanceledError(message, config, request);
      // this.promise 得到解决,状态变更为 fulfilled 
      resolvePromise(token.reason);
    });
  }

  /**
   * 抛出 Error,用于请求过程中的检查
   */
  throwIfRequested() {
    if (this.reason) {
      throw this.reason;
    }
  }

  /**
   * 订阅事件
   */
  subscribe(listener) {
    if (this.reason) {
      listener(this.reason);
      return;
    }

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

  /**
   * 取消事件订阅
   */

  unsubscribe(listener) {
    if (!this._listeners) {
      return;
    }
    const index = this._listeners.indexOf(listener);
    if (index !== -1) {
      this._listeners.splice(index, 1);
    }
  }

  /**
   * 入口逻辑
   */
  static source() {
    let cancel;
    // constructor 内将回调函数传递给 executor,进一步通过 cancel = c 暴露给外部调用者
    // 调用 cancel() 则可以推动中断事件流程
    const token = new CancelToken(function executor(c) {
      cancel = c;
    });
    return {
      token,
      cancel
    };
  }
}

参考内容

  1. AbortController
  2. abortcontroller-polyfill 对比学习,核心内容是:dispatchEventPromise.race([fetch,eventHandlerPromise])