Better

Ethan的博客,欢迎访问交流

axios 中有趣的 cancelToken 实现

axios 中 cancel 请求的具体使用以及原理

具体使用

如果我们需要对 cancel 某请求,则需要传递 config.cancelToken 参数,简单使用如下

const CancelToken = axios.CancelToken
const source = CancelToken.source()
axios.get('/user', {
  cancelToken: source.token
})
// 如果我们不想请求了,则执行如下代码即可
source.cancel('cancel')

如果熟悉原生的 XMLHttpRequest 对象的话,request 对象直接提供了 abort 函数用来取消请求。

但 axios 对使用方式进行了 Promise 化,我们是无法得到 request 对象的。哪 axios 是如何做到的呢

cancelToken 作用

查看使用方式也知道,我们需要对 config 多传递一个特定的 cancelToken 参数,其背后的代码大概是这样的

if (config.cancelToken) {
  config.cancelToken.promise.then(resolve => {
    // axios 内部肯定是可以获取到 request 对象的
    if (!request) {
      return;
    }
    request.abort();
    // 执行 reject
    reject(cancel);
    // Clean up request
    request = null;
  })
}

看到这里好像有点似懂非懂的样子,source.token 有一个 promise 属性,如果该 promise 成功执行,则会执行我们熟悉的 request.abort 操作。由此看来,source.cancel 操作执行的应该是 promise 的 resolve 操作

CancelToken 原理

到这里,其实 CancelToken 的实现原理,大概也就了解的七七八八的,有一个 promise 属性,将该 promise 的 resolve 传递出去作为 cancel 函数。

最简单代码应该如下

function CancelToken() {
  // 执行 resolvePromise 会取消请求
  let resolvePromise
  this.promise = new Promise(resolve => {
    resolvePromise = resolve
  })
}

axios 是将 token 和 cancel 操作分开的,通过一个 source 函数来生成 token 和 cancel 函数作为一个 source 对象。因此对于 token 中的 resolvePromise 如何传递出去呢,答案是通过回调函数。下面先看 source 函数简单实现

CancelToken.prototype.source = function() {
  let cancel
  var token = new CancelToken(function executor(c) {
      cancel = c;
  });
  return {
    token,
    cancel
  }
}

接下来进一步完善 CancelToken 函数

function CancelToken(executor) {
  // 执行 resolvePromise 会取消请求
  let resolvePromise
  this.promise = new Promise(resolve => {
    resolvePromise = resolve
  })
  const token = this;
  executor(function cancel(message) {
    if (token.reason) {
      // 如果已经取消
      return;
    }
    token.reason = new Cancel(message);
    resolvePromise(token.reason);
  });
}

是不是挺简单的哈!



留言