JavaScript 中断请求几种方案详解
作者:想成为大佬的鸭子 发布时间:2024-05-09 10:35:51
1 Promise
Promise有一个缺点是一旦创建无法取消,所以本质上Promise是无法被终止的.
但是我们可以通过中断调用链或中断Promise来模拟请求的中断.
中断调用链
中断调用链就是在某一个then/catch执行之后,后续的链式调用(包括then,catch,finally)不再继续执行.
方法是在then/catch返回一个新的Promise实例,并保持pending状态:
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('result');
});
}).then(res => {
// 达到某种条件,return一个pending状态的Promise实例,以中断调用链
if (res === 'result') {
return new Promise(() => {});
}
console.log(res); // 不打印
}).then(() => {
console.log('then不执行'); // 不打印
}).catch(() => {
console.log('catch不执行'); // 不打印
}).finally(() => {
console.log('finally不执行'); // 不打印
});
中断Promise
中断Promise不等同于中止Promise,因为Promise是无法被终止的.
这里的中断指的是,在合适的时机,把pending状态的promise给reject掉.例如一个常见的应用场景就是给网络请求设置超时时间,一旦超时就中断.
老规矩,用setTimeout来模拟网络请求.阀值设置为Math.random() * 3000表示随机3秒之内返回结果.
const request = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('收到服务端数据')
}, Math.random() * 3000)
})
假设超过2秒就是网络超时,我们可以封装一个超时处理函数.
由于网络请求所需的事件是随机的,因此可以利用Promise.race方法,达到超时reject的目的.
const timeoutReject = (p1, timeout = 2000) => {
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('网络超时');
}, timeout);
});
return Promise.race([p1, p2]);
};
timeoutReject(request).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
});
包装abort方法——仿照Axios的CancelToken
上面实现的方式并不灵活,因为中断Promise的方式有很多,不单单是网络超时.
我们可以仿照Axios中CancelToken的核心源码,简单包装一个abort方法,供使用者随时调用.
function abortWrapper(p1) {
let abort;
const p2 = new Promise((resolve, reject) => {
abort = reject;
});
// 如果没有resolve或reject,p2的状态永远是pending
const p = Promise.race([p1, p2]);
p.abort = abort;
return p;
}
const req = abortWrapper(request);
req.then(res => {
console.log(res);
}).catch(err => {
console.log(err);
});
setTimeout(() => {
// 手动调用req.abort,将p2的状态改变为rejected
req.abort('手动中断请求');
}, 2000);
如此封装的主要目的就是为了能够在Promise外部控制其resolve或reject,让使用者可以随时手动调用resolve(触发.then)或reject(触发.catch).
需要注意的是,虽然Promise请求被中断了,但是promise并没有终止,网络请求依然可能返回,只不过那时我们已经不关心请求结果了.
2 RXJS的unsubscribe方法
rxjs本身提供了取消订阅的方法,即unsubscribe.
let stream1$ = new Observable(observer => {
let timeout = setTimeout(() => {
observer.next('observable timeout');
}, 2000);
return () => {
clearTimeout(timeout);
}
});
let disposable = stream1$.subscribe(value => console.log(value));
setTimeout(() => {
disposable.unsubscribe();
}, 1000);
3 Axios的CancelToken
Axios的CancelToken有两种使用方法:
方法一
import axios from 'axios';
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
}
});
source.cancel('Operation canceled by the user.');
方法二
import axios from 'axios';
const CancelToken = axios.CancelToken;
// 创建一个变量如 cancel 用于存储这个中断某个请求的方法
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
cancel = c; // 将参数 c 赋值给 cancel
})
});
// 判断 cancel 是否为函数,确保 axios 已实例化一个CancelToken
if (typeof cancel === 'function') {
cancel();
cancel = null;
}
CancelToken的核心源码:(axios/lib/cancel/CancelToken.js)
'use strict';
var Cancel = require('./Cancel');
/**
* A `CancelToken` is an object that can be used to request cancellation of an operation.
*
* @class
* @param {Function} executor The executor function.
*/
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
var token = this;
executor(function cancel(message) {
if (token.reason) {
// Cancellation has already been requested
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
/**
* Throws a `Cancel` if cancellation has been requested.
*/
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
if (this.reason) {
throw this.reason;
}
};
/**
* Returns an object that contains a new `CancelToken` and a function that, when called,
* cancels the `CancelToken`.
*/
CancelToken.source = function source() {
var cancel;
var token = new CancelToken(function executor(c) {
cancel = c;
});
return {
token: token,
cancel: cancel
};
};
module.exports = CancelToken;
可以看到,在Axios底层,CancelToken的核心源码所体现的思想,与上面中断Promise包装abort方法的思想一致.
只不过Axios在外部手动调用resolve(用户触发cancel方法),而resolve一旦调用,就会触发promise的then方法,来看这个promise.then的源码:(axios/lib/adapters/xhr.js)
if (config.cancelToken) {
// Handle cancellation
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
request.abort();
reject(cancel);
// Clean up request
request = null;
});
}
可以看到then方法中会执行abort方法取消请求,同时调用reject让外层的promise失败.
来源:https://blog.csdn.net/weixin_41309331/article/details/120180566
猜你喜欢
- 讲这个方法之前,我们应该先了解下插入节点时浏览器会做什么。在浏览器中,我们一旦把节点添加到document.body(或者其他节点)中,页面
- 背景一次工作中,我需要完成某个文件的字符串替换。需求是这样的:文件A有个占位符,需要利用Python3,把占位符替换成文件B的内容。文件都不
- OpenCV图像处理一、图像入门1.读取图像使用 cv.imread() 函数读取一张图像,图片应该在工作目录中,或者应该提供完整的图像路径
- 有时候在使用 Python 的时候,想要对一个数字或者字符串进行补零操作,即把「1」变为一个八位数的「00000001」,这个时候可以使用一
- 01、正则表达式学习正则表达式操作字符串,re模块是用C语言写的没匹配速度非常快,其中compile函数根据一个模式字符串和可选的标志参数生
- TensorFlow是Google公司2015年11月开源的第二代深度学习框架,是第一代框架DistBelief的改进版本. TensorF
- 基于ASP技术开发Internet/Intranet上的MIS系统是非常方便的,首先是它借用了ADO技术和概念,同时
- 一、数据合并与分割1.tf.concat()填入两个tensor, 指定某维度,在指定的维度合并。除了合并的维度之外,其他的维度必须相等。2
- 英文原文:The seven rules of Unobtrusive JavaScript原文地址:http://icant.co.uk/
- concat 与其说是连接,更准确的说是拼接。就是把两个表直接合在一起。于是有一个突出的问题,是横向拼接还是纵向拼接,所以concat 函数
- 通过本篇内容给大家介绍一下Python实现金融数据可视化中两列数据的提取、分别画、双坐标轴、双图、两种不同的图等代码写法和思路总结。impo
- 网上的关于django-scrapy的介绍比较少,该博客只在本人查资料的过程中学习的,如果不对之处,希望指出改正;以后的博客可能不会再出关于
- 开篇这段时间把主要精力都放在了K8S上,差点把Golang给忘了。那本篇就分享一下并发相关的内容(Goroutine和通道)。 本篇给出4个
- 问题你想实现一个服务器,通过TCP协议和客户端通信。解决方案创建一个TCP服务器的一个简单方法是使用 socketserver 库。例如,下
- 几个利用背景结合a:hover做的小东东,希望对大家有所帮助。<!DOCTYPE html PUBLIC "-//W3C//
- Tensorboard详解该类在存放在keras.callbacks模块中。拥有许多参数,主要的参数如下:1、log_dir: 用来保存Te
- 介绍当创建一个应用程序时,通常希望能够告诉你的应用程序如何做某事。有两种流行的方法来完成这项任务,你可以让应用程序接受命令行参数,或者创建一
- 一开始自学Python的numpy、pandas时候,索引和切片把我都给弄晕了,特别是numpy的切片索引、布尔索引和花式索引,简直就是大乱
- 关于变量的命名,这又是一个容易引发程序员论战的话题。如何命名才能更具有可读性、易写性与明义性呢?众说纷纭。本期“Python为什么”栏目,我
- 本文实例讲述了Python实现将Excel转换成xml的方法。分享给大家供大家参考,具体如下:最近写了个小工具 用于excel转成xml直接