axios请求取消步骤源码详解

请求取消,往往使用在当网络较慢,用户在等待当前请求时转而去点了其他请求,且这两个请求是会相互影响时,我们会将当前请求取消,axios提供了请求取消的接口,在github上官方给出了两种实现请求取消的用法

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.');
const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // An executor function receives a cancel function as a parameter
    cancel = c;
  })
});

// cancel the request
cancel();

我们取第一个例子来解析每一步,首先第一步,将axios中的CancelToken赋值给一个变量CancelToken,我们看看axios中的CancelToken

function CancelToken(executor) {
  // 检测传入的是否为函数
  if (typeof executor !== 'function') {
    throw new TypeError('executor must be a function.');
  }

  var resolvePromise;
  // 使用promise异步
  // 在后面调用方法时就会触发promise的resolve
  // 在xhr.js中调用abort方法取消请求
  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });

  var token = this;
  executor(function cancel(message) {
    if (token.reason) {
      // 已经发起过取消请求的情况下,会直接return
      return;
    }

    token.reason = new Cancel(message);
    resolvePromise(token.reason);
  });
}
/**
 * 当请求已经被取消时,抛出Cancel对象
 */
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
  if (this.reason) {
    throw this.reason;
  }
};

/**
 * 返回一个包含新的CancelToken的对象,以及一个用来取消请求的方法
 */
CancelToken.source = function source() {
  var cancel;
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  // 这里的token实际上就是新的CancelToken对象
  // cancel是用来取消请求的方法
  return {
    token: token,
    cancel: cancel
  };
};

这里还不太清楚内部的结构没关系,我们先往下走,这里先知道我们创建了一个CancelToken即可。
看看下一行代码

const source = CancelToken.source();

这里调用了CancelToken的source方法,将返回的值赋值给source,从代码中的source方法

/**
 * 返回一个包含新的CancelToken的对象,以及一个用来取消请求的方法
 */
CancelToken.source = function source() {
  var cancel;
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  // 这里的token实际上就是新的CancelToken对象
  // cancel是用来取消请求的方法
  return {
    token: token,
    cancel: cancel
  };
};

我们可以明确看到,变量source是一个包含新的CancelToken和取消对应token的方法的对象。将executor作为new CancelToken的参数,当我们执行这里的方法c,即返回对象中的cancel时,就会触发下面一系列操作

if (typeof executor !== 'function') {
  throw new TypeError('executor must be a function.');
}

进入CancelToken后,首先判断传入的是不是一个函数,不是的话会抛出错误,这里是一个函数,继续往下走,新建了一个Promise赋值给这个CancelToken对象的promise属性,将resolve赋值给resolvePromise使得我们可以在外部控制promise的状态

var resolvePromise;
// 使用promise异步
// 在后面调用方法时就会触发promise的resolve
// 在xhr.js中调用abort方法取消请求
this.promise = new Promise(function promiseExecutor(resolve) {
  resolvePromise = resolve;
});

在这里,只要我们调用了resolvePromise方法,就等于将this.promise的状态变为resolve,接下来将this赋值给token,将一个cancel方法作为参数传给刚刚传给new CancelToken的参数的方法

var token = this;
executor(function cancel(message) {
  // 已经发起过取消请求的情况下,会直接return
  // 在下一步我们就将请求时会出项错误的信息赋值给token.reason,说明已经发起过这个请求了
  if (token.reason) {
    return;
  }

  token.reason = new Cancel(message);
  resolvePromise(token.reason);
});

当token.reason存在时,即已经执行过这个取消请求的操作了,如果执行过,直接return,如果没执行过,我们将Cancel对象作为token.reason的值,然后将其作为参数执行resolvePromise方法,将promise变为resolve的状态。


Cancel对象实际上就是在请求被取消时会抛出来的错误信息,看看源代码

'use strict';
/**
 * A `Cancel` is an object that is thrown when an operation is canceled.
 *
 * @class
 * @param {string=} message The message.
 */
// 当请求已经被取消的时候再取消会抛出这个Cancel对象作为错误信息
function Cancel(message) {
  this.message = message;
}

Cancel.prototype.toString = function toString() {
  return 'Cancel' + (this.message ? ': ' + this.message : '');
};

Cancel.prototype.__CANCEL__ = true;

module.exports = Cancel;

知道这些后,接下来通过一个请求,我们来将请求时取消请求的具体流程弄清楚

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

在这段代码中,我们使用axios发起了一个get请求,然后将source.token即上面我们调用source方法创建的对象中新建的那个CancelToken传入当前请求的cancelToken属性中,这里我们看看xhr.js中的相关内容

if (config.cancelToken) {
  // 对取消请求的操作
  // 当promise变为resolve时,执行then里的方法
  config.cancelToken.promise.then(function onCanceled(cancel) {
    // 如果请求已经没了,即请求已经结束
    // 那么直接返回
    if (!request) {
      return;
    }
    // 取消请求
    request.abort();
    reject(cancel);
    // 清除请求
    request = null;
  });
}

这里看到,当有cancelToken时,我们给其promise的then里添加相关的操作,而这个操作就是实际上取消请求的具体操作
当没有请求的时候,我们就直接return掉,如果有请求,我们就执行request.abort()来取消请求,将request置为null。
这些操作都在这个promise变为resolve时执行,那么什么时候变成resolve,上面也说过了,调用source返回的那个对象的cancel方法,传入请求被取消时的信息,所以最后一步

source.cancel('Operation canceled by the user.');

这就是整个axios取消请求的具体过程

发布了178 篇原创文章 · 获赞 12 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/zemprogram/article/details/103202132