一文教你如何实现并发请求的失败自动重试及重试次数限制

news2024/11/25 4:46:08

需求

在并发接口请求的时候,能够自动对失败的请求进行重发尝试(超过指定重试次数则不再重试),并将最终的结果返回(包含每个请求是否成功、返回结果)

核心思路

img

代码实现

使用案例

为了演示我们代码的最终实现效果,我们使用如下的案例:

js复制代码function request1() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('请求完毕111');
      resolve(111)
    }, 1000);
  })
}
function request2() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('请求完毕222');
      reject(222)
    }, 2000);
  })
}
function request3() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('请求完毕333');
      reject(333)
    }, 3000)
  })
}

如上存在3个异步请求,我们想要并发发送,并获取到它们的请求结果,理想的方式是这样的:

js复制代码new RequestParallelWithRetry([ request1, request2, request3 ]).send().then((value) => {
  console.log(value);
})

因此,这里我们需要实现一个RequestParallelWithRetry

传入方法标记

根据我们上面提出的思路,我们需要将传入的请求方法进行标记并保存它们的关系,便于后续请求失败时可以利用这个标记来再次调用这个方法

js复制代码class RequestParallelWithRetry {
  requestList
  retryCount
  map = {}
  result = []
  constructor(requestList = [], retryCount = 3) {
    this.requestList = requestList
    this.retryCount = retryCount
    // 需要给每个方法添加标记
    this.requestList.forEach((item, index) => {
      item._index = index
      this.map[ index ] = item
    })
    console.log(this.map);
  }
}

这里我们使用传入的请求列表下标作为标记,并保存起来,打印出来的map结果如下:

img

并发请求发送

之后我们需要实现RequestParallelWithRetry类的send方法,它用来并发发送请求,并且要求能够得知每个请求的执行结果。因为我们这里请求的返回结果都是一个promise对象,因此我们可以利用Promise.allSettled方法来获取所有promise的最终结果:

js复制代码send() {
  const queue = []
  this.requestList.forEach(request => {
    queue.push(request())
  })
  Promise.allSettled(queue).then((value) => {
    console.log('所有请求都完成了', value)
  })
}

此时,调用new RequestParallelWithRetry([ request1, request2, request3 ]).send()将得到如下的打印结果:

img

可以看到:

  • request2request3都请求失败了(返回rejected
  • 我们此时需要做的其实是将这些所有状态为rejected的请求再次执行直到所有的请求都成功(fulfilled)了(先不考虑重试次数限制的情况)
  • 因此,我们需要递归执行这个过程(发送请求->找到失败的请求->再次发送)

包装请求的执行结果

我们已经知道了我们重复执行那些失败的请求直到成功,但是还存在一个问题:现在我们只能知道失败请求的结果,但是我们无法根据这个结果来找到原先执行请求的方法。因此,我们需要对传入的请求进行一层包装,使得执行的返回结果中包含一个标记,我们能根据这个标记找到这个请求方法:

js复制代码send() {
  const queue = []
  this.requestList.forEach(request => {
    queue.push(this._wrapRequest(request))
  })
  Promise.allSettled(queue).then((value) => {
    console.log('所有请求都完成了', value)
  })
}
_wrapRequest(request) {
  return new Promise((resolve, reject) => {
    request().then((value) => {
      resolve({
        value,
        index: request._index
      })
    }).catch(reason => {
      reject({
        reason,
        index: request._index
      })
    })
  })
}

此时,我们就能在请求结果中获取到原先请求方法的标记index了,利用这个标记和已经保存过的map关系映射,就能获取到原先的请求方法,从而再次调用

img

请求失败再次调用

js复制代码send() {
  let queue = []
  this.requestList.forEach(request => {
    queue.push(this._wrapRequest(request))
  })
  const sendAllSettled = () => {
    Promise.allSettled(queue).then((value) => {
      queue = []
      console.log('所有请求都完成了', value);
      // 检查是否存在失败的请求
      value.forEach(result => {
        if (result.status === "rejected") {
          const { reason, index } = result.reason
          const request = this.map[ index ]
          queue.push(this._wrapRequest(request))
        }
      })
      //  检查队列中是否还需要发送请求
      if (queue.length) {
        sendAllSettled()
      }
    })
  }

  sendAllSettled()
}

这里,我们在send方法内部递归调用sendAllSettled方法,sendAllSettled方法做的事情很简单:

  1. queue队列中的请求发送出去
  2. 将失败的请求重新添加到队列中,再次发送

重试次数限制

在上面的代码中,我们已经实现了并发请求失败时自动重新尝试,但是在实际开发中,我们并不能让它无限制地重试下去,而是应该指定重试的次数,一旦超过这个次数了,就不再重试

具体的实现也很简单,只需要给每个请求方法添加一个记录重试次数的属性_retryCount即可,一旦这个_retryCount超过retryCount就不再重试了

具体代码如下:

js复制代码send() {
  let queue = []
  this.requestList.forEach(request => {
    request._retryCount = 0
    queue.push(this._wrapRequest(request))
  })
  const sendAllSettled = () => {
    Promise.allSettled(queue).then((value) => {
      queue = []
      console.log('所有请求都完成了', value);
      // 检查是否存在失败的请求
      value.forEach(result => {
        if (result.status === "rejected") {
          const { reason, index } = result.reason
          const request = this.map[ index ]
          if (request._retryCount < this.retryCount) {
            queue.push(this._wrapRequest(request))
            request._retryCount++
          }
        }
      })
      //  检查队列中是否还需要发送请求
      if (queue.length) {
        sendAllSettled()
      }
    })
  }

  sendAllSettled()
}

并发请求执行结果返回

上面的代码中,我们已经实现了并发请求的失败重试以及重试次数的限制了,最后我们需要将并发请求的最终执行结果返回给用户

由于并发请求是一系列的异步操作,因此我们对于send方法的返回结果也应该是一个promise对象,这个对象包含了每个请求的执行结果(成功或失败)和返回结果

具体实现如下:

js复制代码send() {
  return new Promise((resolve) => {
    // 1. 将所有的网络请求进行包装,使得其返回结果中携带有_index
    // 2. 发送所有的网络请求
    let queue = []
    this.requestList.forEach(request => {
      // 3. 记录每个请求的重试次数
      request._retryCount = 0
      queue.push(this._wrapRequest(request))
    })

    const sendAllSettled = () => {
      Promise.allSettled(queue).then((value) => {
        // 3. 清除发送队列
        queue = []
        console.log('所有请求都完成了', value);
        // 4. 检查是否存在失败的请求
        value.forEach(result => {
          if (result.status === "fulfilled") {
            const { value, index } = result.value
            this.result[ index ] = { value, status: 'fulfilled' }
          }
          else if (result.status === "rejected") {
            const { reason, index } = result.reason
            const request = this.map[ index ]
            if (request._retryCount < this.retryCount) {
              queue.push(this._wrapRequest(request))
              request._retryCount++
            } else {
              this.result[ index ] = { reason, status: 'rejected' }
            }
          }
        })
        //  5. 检查队列中是否还需要发送请求
        if (queue.length) {
          sendAllSettled()
        } else {
          resolve(this.result)
        }
      })
    }

    sendAllSettled()

  })
}

最终用户的调用形式和执行结果会是这样的:

js复制代码new RequestParallelWithRetry([ request1, request2, request3 ]).send().then((value) => {
  console.log(value);
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1813200.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

期权交易单位是什么?期权懂新手必看!

今天带你了解期权交易单位是什么&#xff1f;很多对期权还不太熟悉的朋友&#xff0c;不知道期权的单位是什么&#xff0c;下面小编就来告诉你期权的交易单位到底是什么&#xff1f; 期权交易单位是什么&#xff1f; 50ETF期权的交易单位&#xff0c;用大白话来说&#xff0c;…

从盛世到衰落,历史上八大强国的兴衰与现代地位!

人类文明史悠久&#xff0c;从远古时代至今日&#xff0c;世界舞台上曾经涌现出许多强盛的帝国。它们在自己的黄金时代&#xff0c;曾经无人能敌&#xff0c;不论是在军事、经济还是文化上都独领风骚。然而&#xff0c;无论多么强大的国家也难逃“兴盛必衰”的命运。今天&#…

【文档+源码+调试讲解】精准扶贫管理系统

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了精准扶贫管理系统的开发全过程。通过分析精准扶贫管理系统管理的不足&#xff0c;创建了一个计算机管理精准扶贫管理系统的方案。文章介绍了精准扶贫管理系统的系…

【Week-R3】天气预测,引入探索式数据分析方法(EDA)

文章目录 1. 导入模块2. 导入数据3.探索式数据分析方法&#xff08;EDA&#xff09;3.1 数据相关性探索3.2 是否会下雨3.3 地理位置与下雨的关系3.4 湿度和压力对下雨的影响3.5 气温对下雨的影响 4.数据预处理4.1 处理缺损值4.2 构建数据集 5 预测是否会下雨5.1 构建神经网络5.…

JAVAEE值之网络原理(1)_传输控制协议(UDP)、概念、特点、结构、代码实例

前言 在前两节中我们介绍了UDP数据报套接字编程&#xff0c;但是并没有对UDP进行详细介绍&#xff0c;本节中我们将会详细介绍传输层中的UDP协议。 一、什么是UDP&#xff1f; UDP工作在传输层&#xff0c;用于程序之间传输数据的。数据一般包含&#xff1a;文件类型&#xff0…

您对薪资待遇是否满意?没证据怎么办?这样做很可能会补上来!

您对薪资待遇是否满意&#xff1f;没证据怎么办&#xff1f; 这样做很可能会补上来&#xff01; 您有时可能对自己的工资或福利待遇感到不满意&#xff1a;感到为何我付出的不比别人少&#xff0c;但是工资待遇总是比别人低&#xff0c;是不是觉得很不服气&#xff1f;那么不服…

K8s 卷快照类

卷快照类 卷快照类 这个警告信息通常出现在使用 kubectl 删除 Kubernetes 集群资源时&#xff0c;如果尝试删除的是集群作用域&#xff08;cluster-scoped&#xff09;的资源&#xff0c;但指定了命名空间&#xff08;namespace&#xff09;&#xff0c;就会出现这个警告。 集…

leetcode刷题-二叉树02

代码随想录二叉树part02|102.层序遍历、226.翻转二叉树、101.对称二叉树 102.层序遍历--十题226.翻转二叉树101.对称二叉树 102.层序遍历–十题 代码随想录文档讲解 LeetCode102 图论中的深度搜索和广度搜索分别对应二叉树中的递归遍历和层序遍历。 3/ \9 20/ \15 7返回层序…

ubuntu 20.04禁止自动更新内核驱动、显卡驱动(使用命令行)

本文目录 一、禁止更新内核1.1 查看当前内核1.2 查看安装的内核1.3 根据需求&#xff0c;使用hold参数禁止固定内核1.4 查询被锁定不更新软件包的状态 二、禁止更新显卡驱动2.1 查看安装的显卡驱动2.2 查看详细的详细的显卡信息2.3 禁止显卡驱动更新2.4 查询显卡是否设置成功 前…

记录大三上学期大数据课程设计:基于Hadoop和Spark的中文手写数字实时识别系统

我整理好了两个百度网盘链接&#xff0c;一个是模型文档和数据&#xff0c;一个是镜像&#xff0c;下载、导入虚拟机即可运行。 github地址&#xff1a;Li-Jihong/big-data: 用来记录大三上学期大数据课程设计&#xff1a;基于Hadoop和Spark的中文手写数字实时识别系统 (githu…

c# 二维图形绘制实践

1.等边三角形 1.1 概述 1.2 代码 using System; using System.Drawing; using System.Windows.Forms;public partial class TriangleForm : Form {public TriangleForm(){//InitializeComponent();// 确保窗体大小足够大&#xff0c;以容纳三角形 this.ClientSize new Siz…

计算机网络(1) OSI七层模型与TCP/IP四层模型

一.OSI七层模型 OSI 七层模型是国际标准化组织ISO提出的一个网络分层模型&#xff0c;它的目的是使各种不同的计算机和网络在世界范围内按照相同的标准框架实现互联。OSI 模型把网络通信的工作分为 7 层&#xff0c;从下到上分别是物理层、数据链路层、网络层、传输层、会话层、…

小魔推-短视频矩阵批量创作一键分发同城引流工具

​小魔推是一款短视频营销裂变推广工具&#xff0c;主要服务于想做短视频营销的实体商家&#xff0c;通过BGC、PGC、UGC的打造帮助商家实现流量裂变与转化。 其中&#xff0c;小魔推AI矩阵营销是借助AI技术帮助企业/商家搭建营销矩阵&#xff0c;让商家拥有足够多的账号、足够…

matlab-2-simulink-小白教程-如何绘制电路图进行电路仿真

以上述电路图为例&#xff1a;包含D触发器&#xff0c;时钟CLK,与非门 一、启动simulink的三种方式 方式1 在MATLAB的命令行窗口输入“Simulink”命令。 方式2 在MATLAB主窗口的“主页”选项卡中&#xff0c;单击“SIMULINK”命令组中的Simulink命令按钮。 方式3 从MATLAB…

最小二乘法原理及其代码实现

一、最小二乘法原理 假设目前我们有一些数据&#xff0c;x是输入&#xff0c;y是与之对应的输出。现在想利用这些已有的数据&#xff0c;从中发现出规律&#xff0c;来预测没有出现过的输入会产生什么样的输出。 假设系统为单输入单输出系统&#xff0c;我们想在这个系统里找到…

【TB作品】MSP430F5529 单片机,数字时钟设计与实现,整点时通过蜂鸣器播放音乐进行报时

基于单片机的数字时钟设计与实现 作品名称 基于MSP430单片机的OLED显示数字时钟 作品功能 本作品实现了一个具有时间显示和整点报时功能的数字时钟。通过OLED屏幕显示当前时间&#xff0c;用户可以通过按键设置时间&#xff0c;并在整点时通过蜂鸣器播放音乐进行报时。 作…

Serif Affinity 2.5 (macOS, Windows) - 专业创意软件

Serif Affinity 2.5 (macOS, Windows) - 专业创意软件 Affinity Designer 2, Affinity Photo 2, Affinity Publisher 2 请访问原文链接&#xff1a;Serif Affinity 2.5 (macOS, Windows) - 专业创意软件&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主…

如何优化仓库布局与ERP库存管理

一、引言 随着企业规模的不断扩大&#xff0c;仓库管理和库存控制成为企业运营中不可或缺的一环。优化仓库布局和提高ERP库存管理效率&#xff0c;对于降低企业成本、提高物流效率、增强企业竞争力具有重要意义。 二、优化仓库布局 1. 分析仓库需求 在优化仓库布局之前&…

【学习笔记】Linux

Linux 1、 介绍 1.1、概述 1.2、特点 1.3、Linux的发行版2、 基础篇 —— 文件系统 2.1、文件系统 2.2、目录结构3、 基础篇 —— VI/VIM 编辑器 3.1、概述 3.2、编辑器模式及常用命令4、 基础篇 —— 网络配置 4.1、VMware NetWork …

【Linux】shell——条件判断test,各种运算符,expr

条件判断——test 真——0 假——1 test expression or [ expression ] 整数运算符 字符串运算符 -z 长度是否为0 -n 长度是否不为0 str1 str2 str1 ! str2 补 &&-->逻辑与&#xff0c;前面为真后面才会执行 || -->逻辑或&#xff0c;前面为假后面才…