JavaScript 中断请求几种方案详解

目录
  • 1 Promise
    • 中断调用链
    • 中断Promise
    • 包装abort方法——仿照Axios的CancelToken
  • 2 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中断请求内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 利用js实现Ajax并发请求限制请求数量的示例代码

    出现问题描述:当不确定异步请求个数时,为防止当一瞬间发生上百个http请求时,导致堆积了无数调用栈进而导致内存溢出问题. 要求:将同一时刻并发请求数量控制在3个以内,同时还要尽可能快速的拿到响应的结果. 同面试问题: 实现一个批量请求函数 multiRequest(urls, maxNum),要求如下: 要求最大并发数 maxNum 每当有一个请求返回,就留下一个空位,可以增加新的请求 所有请求完成后,结果按照 urls 里面的顺序依次打出 1.基于Promise.all实现Ajax的串行和并行

  • JavaScript如何利用Promise控制并发请求个数

    一.场景 假设现在有这么一种场景:现有 30 个异步请求需要发送,但由于某些原因,我们必须将同一时刻并发请求数量控制在 5 个以内,同时还要尽可能快速的拿到响应结果. 如图所示: 上图这样的排队和并发请求的场景基本类似,窗口只有三个,人超过三个之后,后面的人只能排队了. 二.串行和并行 串行:一个异步请求完了之后在进行下一个请求 并行:多个异步请求同时进行 串行举例: var p = function () { return new Promise(function (resolve, reje

  • PHP实现chrome表单请求数据转换为接口使用的json数据

    为什么要写转换程序 最近在做旧版程序迁移,旧的架构为常规的MVC模式,新版架构全部改成restful架构. 由于改版数据是一致的,但是请求结构不一致,新版的请求全部以json形式提交,为了方便测试,之前一直都是直接在浏览器打开开发者工具,然后把请求内容复制过来,然后手动改成json形式,由于之前数据量比较少,暂时未发现测试时候不方便的情况,但是今天遇到了数据比较多的情况,于是我想,为啥不写一段转换程序呢? 本身来说,程序的初衷应该是提高工作效率,这也是我之前缺少反思的地方,因此写下这篇文章做个记

  • js实现axios限制请求队列

    目录 背景是: 会造成什么情况呢? 背景是: 在实际开发中,可能会遇到网络问题或者查询量比较大的情况,上一个请求还没有完成,用户就发起了下一个请求. 会造成什么情况呢? 但是同一个请求多次发送到服务器,无疑是对服务器的一种压力,所以需要在已经优化服务器过查询速度后,以及用户网络情况比较差的条件下,在前端进行请求限制. axios 自带的cancelToken可以帮我们实现这个需求,并且提供给了我们一个现成的api axios.CancelToken ,这是一个返回值是带有请求信息的回调函数,我们

  • JS 实现请求调度器

    前言:JS 天然支持并行请求,但与此同时会带来一些问题,比如会造成目标服务器压力过大,所以本文引入"请求调度器"来节制并发度. TLDR; 直接跳转『抽象和复用』章节. 为了获取一批互不依赖的资源,通常从性能考虑可以用 Promise.all(arrayOfPromises)来并发执行.比如我们已有 100 个应用的 id,需求是聚合所有应用的 PV,我们通常会这么写: const ids = [1001, 1002, 1003, 1004, 1005]; const urlPrefi

  • gin 获取post请求的json body操作

    我就废话不多说了,大家还是直接看代码吧~ 代码如下 type KDRespBody struct { Errcode int `json:"errcode"` Desc string `json:"description"` Data []services.KdSearchBack `json:"data"` } var reqInfo KDRespBody err := c.BindJSON(&reqInfo) if err != ni

  • JavaScript 中断请求几种方案详解

    目录 1 Promise 中断调用链 中断Promise 包装abort方法--仿照Axios的CancelToken 2 RXJS的unsubscribe方法 3 Axios的CancelToken 1 Promise Promise有一个缺点是一旦创建无法取消,所以本质上Promise是无法被终止的. 但是我们可以通过中断调用链或中断Promise来模拟请求的中断. 中断调用链 中断调用链就是在某一个then/catch执行之后,后续的链式调用(包括then,catch,finally)不再

  • Python实现定时任务的八种方案详解

    目录 利用whileTrue:+sleep()实现定时任务 使用Timeloop库运行定时任务 利用threading.Timer实现定时任务 利用内置模块sched实现定时任务 利用调度模块schedule实现定时任务 利用任务框架APScheduler实现定时任务 APScheduler中的重要概念 Job作业 Trigger触发器 Executor执行器 Jobstore作业存储 Event事件 调度器 Scheduler的工作流程 使用分布式消息系统Celery实现定时任务 使用数据流工

  • Java实现监听文件变化的三种方案详解

    目录 背景 方案一:定时任务 + File#lastModified 方案二:WatchService 方案三:Apache Commons-IO 小结 背景 在研究规则引擎时,如果规则以文件的形式存储,那么就需要监听指定的目录或文件来感知规则是否变化,进而进行加载.当然,在其他业务场景下,比如想实现配置文件的动态加载.日志文件的监听.FTP文件变动监听等都会遇到类似的场景. 本文给大家提供三种解决方案,并分析其中的利弊,建议收藏,以备不时之需. 方案一:定时任务 + File#lastModi

  • MySQL 快速删除大量数据(千万级别)的几种实践方案详解

    笔者最近工作中遇见一个性能瓶颈问题,MySQL表,每天大概新增776万条记录,存储周期为7天,超过7天的数据需要在新增记录前老化.连续运行9天以后,删除一天的数据大概需要3个半小时(环境:128G, 32核,4T硬盘),而这是不能接受的.当然如果要整个表删除,毋庸置疑用 TRUNCATE TABLE就好. 最初的方案(因为未预料到删除会如此慢),代码如下(最简单和朴素的方法): delete from table_name where cnt_date <= target_date 后经过研究,

  • JavaScript生成UUID的五种方法详解

    目录 简介 1.第一种 2.第二种 3.第三种 4.第四种 5.第五种 简介 UUID(Universally Unique IDentifier) 全局唯一标识符. UUID是一种由算法生成的二进制长度为128位的数字标识符.UUID的格式为“xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”,其中的 x 是 0-9 或 a-f范围内的一个32位十六进制数.在理想情况下,任何计算机和计算机集群都不会生成两个相同的UUID. 1.第一种 function guid() {

  • JavaScript中数组去重常用的五种方法详解

    目录 1.对象属性(indexof) 2.new Set(数组) 3.new Map() 4.filter() + indexof 5.reduce() + includes 补充 原数组 const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}]; 1.对象属性(indexof) 利用对象属性key排除重复项 遍历数组,每次判断新数组中是否存在该属性,不存在就存储在新数组中 并把数组元素作为key,最后返

  • 基于rem的移动端响应式适配方案(详解)

    视口 在前一段时间,我曾经写过一篇关于viewport的文章.最近由于在接触移动端开发,对viewport有了新的理解.于是,打算重新写一篇文章,介绍移动端视口的相关概念. 关于这篇文章说到的所有知识,本质上离不开以下代码 <meta name="viewport" content="width=device-width, initial-scala=1, maximum-scale=1, minimum-scale=1, user-scalable=no"

  • React Native采用Hermes热更新打包方案详解

    目录 1, 背景 2,热更新传统方案 3,使用Hermes打包 1, 背景 如果我们打开RN的Android源码,在build.gradle中回看到这样一段代码. if (enableHermes) { def hermesPath = "../../node_modules/hermes-engine/android/"; debugImplementation files(hermesPath + "hermes-debug.aar") releaseImple

  • rollup输出的6种格式详解

    目录 学习本文 为什么要学这个? DEMO与示例构建 一.IIFE 自执行函数 1.1 打包结果分析 1.2 如何运行 1.3 优缺点 二.CommonJS 2.1 分析打包结果 2.2 如何运行 2.3 优缺点 三.AMD 和 requirejs ! 3.1 打包结果分析 3.2 如何运行 3.3 优缺点 四.UMD 伟大的整合 4.1 打包分析 4.2 如何运行? 4.3 优缺点 五.SystemJs 六.ESM 6.1 打包分析 6.2 如何运行 总结:分别适合在什么场景使用? 学习本文

  • 前端JS图片懒加载原理方案详解

    目录 背景 原理 方案 方案一:img的loading属性设为“lazy” 使用方法 优点 兼容性 缺点 方案二:通过offsetTop来计算是否在可视区域内 优化 优点 缺点 方案三:通过getBoundingClientRect来计算是否在可视区域内 方案四:使用IntersectionObserver来判断是否在可视区域内 兼容性 优点 缺点 问题 布局抖动 响应式图片 SEO不友好 插件 背景 懒加载经常出现在前端面试中,是前端性能优化的常用技巧.懒加载也叫延迟加载,把非关键资源先不加载

随机推荐