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

标签:js,请求,中断
0
投稿

猜你喜欢

  • DTS构建组件及其如何完成数据转换服务

    2009-01-20 15:37:00
  • 原生Javascript插件开发实践

    2024-04-17 09:43:45
  • http请求405错误方法不被允许的解决 (Method not allowed)

    2022-10-12 11:12:10
  • java数据库开发之JDBC基础使用方法及实例详解

    2024-01-21 04:37:00
  • Python enumerate函数遍历数据对象组合过程解析

    2023-09-22 02:51:06
  • python itchat实现调用微信接口的第三方模块方法

    2023-03-24 07:12:53
  • Python使用pptx实现复制页面到其他PPT中

    2021-07-13 01:28:16
  • Django用户认证系统如何实现自定义

    2021-02-03 07:54:38
  • 如何使用PHP中的字符串函数

    2024-05-11 10:01:52
  • vue表单数据交互提交演示教程

    2024-04-10 13:48:08
  • 简述Python2与Python3的不同点

    2023-03-25 10:22:10
  • Python深拷贝浅拷贝图文示例清晰整理

    2022-05-05 11:26:40
  • python验证码图片处理(二值化)

    2023-09-14 15:29:02
  • Windows10下mysql 5.7.21 Installer版安装图文教程

    2024-01-18 10:52:23
  • Python的Django框架中消息通知的计数器实现教程

    2021-03-22 04:13:43
  • javascript编写贪吃蛇游戏

    2024-04-22 22:31:00
  • python爬虫入门教程之点点美女图片爬虫代码分享

    2021-01-14 21:45:07
  • Dreamweaver制作网页幻灯片效果

    2010-07-06 13:48:00
  • ASP+Access数据库安全设置方法小结

    2011-04-02 11:09:00
  • MySQL Explain命令用于查看执行效果

    2009-02-27 15:30:00
  • asp之家 网络编程 m.aspxhome.com