手写axios源码系列五:CancelToken取消请求类封装

在这里插入图片描述


在第一篇章节 手写axios源码系列一:axios核心知识点 中,我们知道取消请求有两种配置方式:

  1. config.cancelToken
  2. config.signal

两种方式都可以取消请求,接下来我们探究一下两种取消请求的方式有何不同。

一、使用 CancelToken 取消请求

config.cancelToken 取消请求的方式是封装 CancelToken 类,使用发布/订阅模式对发送的请求进行了订阅 onCanceled 方法,然后在需要时执行 onCanceled 方法取消请求。

1、创建 CancelToken.js 文件

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、发送请求时订阅 onCanceled 方法

什么时候订阅取消请求 onCanceled 方法呢?
发送请求应该与取消请求是相耦合的,所以应该在发送请求的时候订阅取消当前请求的方法 onCanceled 。

发送请求的代码写在 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);
		}
	})
}

二、使用 AbortController 取消请求

使用 AbortController 的方式取消请求代码很简单,根据 config 对象中是否配有 signal 字段来判断,如果有则取消请求,没有则不做任何操作。

修改 xhr.js 文件代码:

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);
			}
		}
	})
}

三、使用 json-server 测试"取消请求"功能代码

json-server 是一个可以在前端本地运行,存储 json 数据格式的快速启用的服务器。可以说它就是一个 mock 数据服务,可以模仿RESTful API。

1、全局安装 json-server

npm i -g json-server

2、创建 db.json 文件并监听文件

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

监听指令:json-server --watch db.json

因为我们这里要测试取消请求,所以给请求添加个延迟时间,容易操作。

执行指令:json-server --watch -d 2000 db.json,请求会在延迟2s后返回响应数据。

3、创建 index.html 测试代码

<!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>

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值