Skip to main content
  1. Blogs/
  2. 前端开发/

·228 words·2 mins
Table of Contents
const CancelToken = axios.CancelToken;

// 1. 定义退出对象
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
  }
});

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

通过 source.cancel 来触发取消请求,source 通过如下方法获得:

class CancelToken {
	constructor(executor) {
		//...
	    executor(function cancel(message, config, request) {
	      if (token.reason) {
	        // Cancellation has already been requested
	        return;
	      }
	
	      token.reason = new CanceledError(message, config, request);
	      resolvePromise(token.reason);
	    });
	}
	// ...
	static source() {
		let cancel;
		const token = new CancelToken(function executor(c) {
		  cancel = c;
		});
		return {
		  token,
		  cancel
		};
	}
}

cancel 是通过 new CancelToken 闭包的引用,其中的 c 便是 executor 的回调方法 cancel。

let resolvePromise;

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

const token = this;

// eslint-disable-next-line func-names
this.promise.then(cancel => {
  if (!token._listeners) return;

  let i = token._listeners.length;

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

在 executor 会调用 resolvePromise 从而逐步将 _listeners 调用取消,从而触发到 xhr.abort。

// lib\adapters\xhr.js
if (_config.cancelToken || _config.signal) {
  // Handle cancellation
  // eslint-disable-next-line func-names
  onCanceled = cancel => {
	if (!request) {
	  return;
	}
	reject(!cancel || cancel.type ? new CanceledError(null, config, request) : cancel);
	request.abort();
	request = null;
  };

  _config.cancelToken && _config.cancelToken.subscribe(onCanceled);
  if (_config.signal) {
	_config.signal.aborted ? onCanceled() : _config.signal.addEventListener('abort', onCanceled);
  }
}

对比 AbortController 方式
#

const controller = new AbortController();
axios.get('/foo/bar', {
   signal: controller.signal
});
controller.abort();
  • 更好的拥抱 web 标准
  • 对比 cancelToken 操作更加便捷