Handwritten axios source code series five: CancelToken cancellation request class encapsulation

Insert image description here


in the first chapter Handwritten axios source code series one: axios core knowledge points In, we know that there are two configuration methods for canceling requests:

  1. config.cancelToken
  2. config.signal

Both methods can cancel the request. Next, let's explore the differences between the two methods of canceling the request.

1. Use CancelToken to cancel the request

The way config.cancelToken cancels the request is to encapsulate the CancelToken class, use the publish/subscribe mode to subscribe to the onCanceled method for the sent request, and then execute the onCanceled method to cancel the request when needed.

1. Create the CancelToken.js file

export default class CancelToken {
    
    
	constructor(executor){
    
    
		let resolvePromise;
		// 给实例对象添加 promise属性
		this.promise = new Promise(function promiseExecutor(resolve){
    
    
			// 暴露 resolve方法,可以在外部执行 resolvePromise方法改变当前 pending状态的 promise为 fulfilled
			resolvePromise = resolve;
		})
		// 调用执行器函数,传入一个函数
		executor(function c(){
    
    
			resolvePromise()
		})
		// 当 this.promise状态变更时执行取消请求订阅事件
		this.promise.then(() => {
    
    
			const i = this._listeners.length;
			// 循环数组倒序执行
			while(i-- > 0){
    
    
				this._listeners[i]();  // 执行取消请求 onCanceled方法
			}
			// 置空 _listeners
			this._listeners = null;
		})
	}
	// 订阅事件方法 subscribe
	subscribe(listener){
    
    
		if (this._listeners){
    
    
			// 向 _listeners中添加监听函数
			this._listeners.push(listenner)
		} else {
    
    
			// 创建 this._listeners容器
			this._listeners = [listener]
		}
	}
	// 静态方法 source,其实是一个工厂函数
	static source(){
    
    
		let cancel;
		// token为 CancelToken的实例对象,带有 subscribe订阅方法
		const token = new CancelToken(function executor(c){
    
    
			cancel = c;
		});
		return {
    
     token, cancel }
	}
}

2. Subscribe to the onCanceled method when sending a request

When will you subscribe to the onCanceled method of the cancellation request?
Sending a request should be coupled with canceling the request, so you should subscribe to the method onCanceled to cancel the current request when sending the request.

The code to send the request is written under xhr.js:

export default function xhrAdapter(config) {
    
    
	// 返回一个 promise对象
	return new Promise((resolve, reject) => {
    
    
		// 其他代码
		// ...
		// 发送请求
		request.send(config.data || null);
		// 订阅取消请求,根据 config中是否配置了 cancelToken字段来判断是否需要取消请求
		if (config.cancelToken) {
    
    
			const onCanceled = () => {
    
    
				request.abort();  // 中止请求
			}
			// 订阅取消请求 onCanceled方法
			config.cancelToken.subscribe(onCanceled);
		}
	})
}

2. Use AbortController to cancel the request

The code to cancel the request using AbortController is very simple. It is judged according to whether there is a signal field in the config object. If there is a signal field, the request will be canceled. If not, no action will be taken.

Modify the xhr.js file code:

export default function xhrAdapter(config) {
    
    
	// 返回一个 promise对象
	return new Promise((resolve, reject) => {
    
    
		// 其他代码
		...
		// 发送请求
		request.send(config.data || null);
		// 订阅取消请求,根据 config中是否配置了 cancelToken字段或者 signal字段来判断是否需要取消请求
		if (config.cancelToken || config.signal) {
    
    
			const onCanceled = () => {
    
    
				request.abort();  // 中止请求
			}
			// 订阅取消请求 onCanceled方法
			config.cancelToken && config.cancelToken.subscribe(onCanceled);
			// signal字段判断
			if (config.signal){
    
    
				/**
				 * const controller = new AbortController()
				 * signal是 controller.signal对象,带有三个属性
				 * signal:{ 
				 * 		aborted: false,  	通信请求是否被终止(true)或未终止(false),只读属性。
				 * 		onabort: null,   	监听事件。
				 * 		reason: undefined	取消请求原因。
				 * } 
				 */
				// aborted 字段为 true时直接取消请求,否则添加 abort监听取消事件
				config.signal.aborted ? onCanceled() : config.signal.addEventListener("abort", onCanceled);
			}
		}
	})
}

3. Use json-server to test the "cancel request" function code

json-server is a fast-enabled server that can be run locally on the front end and store json data format. It can be said that it is a mock data service that can imitate RESTful API.

1. Install json-server globally

npm i -g json-server

2. Create the db.json file and monitor the file

{
    
    
  "posts": [
    {
    
    
      "id": 1,
      "title": "json-server",
      "author": "typicode"
    },
    {
    
    
      "id": 2,
      "title": "json-server2",
      "author": "typicode2"
    }
  ]
}

Listening command:json-server --watch db.json.

Because we want to test the cancellation request here, we add a delay time to the request for easy operation.

Execute command:json-server --watch -d 2000 db.json, the request will return response data after a delay of 2s.

3. Create index.html test code

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>axios源码解析</title>
</head>
<body>
  <button id="request">请求接口</button>
  <button id="cancel">取消请求</button>

  <script type="module">
    import axios from "./axios.js";
    const requestBtn = document.querySelector("#request");
    const cancelBtn = document.querySelector("#cancel");
    let source = null;
    // let controller = null

    requestBtn.addEventListener("click", function () {
      
      
      // controller = new AbortController();
      source = axios.CancelToken.source();
      // 发送请求
      axios({
      
      
        method: "get",
        url: "http://localhost:3000/posts",
        // signal: controller.signal,
        cancelToken: source.token
      }).then(response => {
      
      
        console.log(response);
      })
    });
    // 取消请求
    cancelBtn.addEventListener("click", function () {
      
      
   	  // controller.abort();
      source.cancel();
    });
  </script>
</body>
</html>

Insert image description here

Guess you like

Origin blog.csdn.net/ThisEqualThis/article/details/130323866