千家信息网

JavaScript如何中断请求

发表于:2025-01-25 作者:千家信息网编辑
千家信息网最后更新 2025年01月25日,这篇文章给大家分享的是有关JavaScript如何中断请求的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1 PromisePromise有一个缺点是一旦创建无法取消,所以本
千家信息网最后更新 2025年01月25日JavaScript如何中断请求

这篇文章给大家分享的是有关JavaScript如何中断请求的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

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 已实例化一个CancelTokenif (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如何中断请求"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

0