JavaScript 中断请求几种方案详解

JavaScript 中断请求几种方案详解

目录1 Promise中断调用链中断Promise包装abort方法——仿照Axios的CancelToken2 RXJS的unsubscribe方法3 Axios的CancelToken

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失败.

到此这篇关于JavaScript 中断请求几种方案详解的文章就介绍到这了,更多相关js中断请求内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:写入cookie的JavaScript代码库 cookieLibrary.js从基础开始建立一个JS代码库java FastJson的简单用法Vue.js性能优化N个技巧(值得收藏)angularJS实现表格部分列展开缩起示例代码JavaScript实现简易放大镜最全代码解析(ES5)JavaScript实现简易轮播图最全代码解析(ES6面向对象)JavaScript实现简易轮播图最全代码解析(ES5)JavaScript实现简易购物车最全代码解析(ES6面向对象)5种方法告诉你如何使JavaScript 代码库更干净

相关文章

花木兰技能怎么用(王者荣耀花木兰技能解析)
bet3365官方手机

花木兰技能怎么用(王者荣耀花木兰技能解析)

🕒 07-07 👁️ 9514
[分享]【攻略:耕耘礼包】SNK联动臻选礼包入手指南,重点道具选择/套数分配/回血建议
卡西欧 casio 手表 PRO TREK 液晶系列 PRG-270-1
best365从哪能进去

卡西欧 casio 手表 PRO TREK 液晶系列 PRG-270-1

🕒 07-13 👁️ 4614
媲美旗舰级Core i7的规格!锐龙 AMD Ryzen 7的超线程来了
best365从哪能进去

媲美旗舰级Core i7的规格!锐龙 AMD Ryzen 7的超线程来了

🕒 07-16 👁️ 2330
如何给好友推荐微信群
365bet足球网址

如何给好友推荐微信群

🕒 07-04 👁️ 9707
如何给好友推荐微信群
365bet足球网址

如何给好友推荐微信群

🕒 07-04 👁️ 9707