当前位置: 首页 > news >正文

使用 TypeScript 的指数退避机制包装异步请求

使用 TypeScript 的指数退避机制包装异步请求

在进行网络请求时,很有可能会遇到某些临时失败,例如网络波动、请求超时、服务器端未响应等。面对这种情况,最好的做法往往是实施一种重试机制,而指数退避(Exponential Backoff) 是一种非常流行且有效的重试策略。它通过递增间隔时间来避免系统过度拥塞,提高成功执行的几率。

本文将基于 TypeScript 和 Axios 构造一个适用于任意 API 请求的通用 Promise 包装函数,并包含支持指数退避重试的功能。


什么是指数退避?

指数退避是通过延迟策略实现的一种算法,其核心是随着重试次数的增加,系统会按照展示增长的方式增加每次重试前等待的时间。公式如下:

delay = baseDelay * (2 ^ attempt)

假如 baseDelay 为 1000 毫秒,第一次重试后等待 1 秒,第二次重试等待 2 秒,第三次等待 4 秒……延迟的时间会迅速增大。

这种机制可以有效减少系统的负载压力,同时也可以应对一些临时性的问题,比如网络波动、瞬时连接错误等。


指数退避的代码实现

我们使用 axios 库作为 HTTP 客户端,通过包装一个请求函数,并在请求失败时实现可配置的重试机制。下面是完整版代码:

/*** 包装一次请求,支持指数退避的自动重试。 * @param client 已配置的 Axios 实例* @param requestFn 实际执行请求的函数,应该返回一个 Promise* @param maxRetries 最大重试次数,默认2次* @param baseDelay 基础延迟时间,单位毫秒,默认1000ms* @returns 请求成功时的响应数据* @throws 最后一次请求失败的错误*/
async function requestWithRetry<T>(client: AxiosInstance,requestFn: () => Promise<T>,maxRetries = 2,baseDelay = 1000
): Promise<T> {let lastError: any; // 在尝试和重试之间循环for (let attempt = 0; attempt <= maxRetries; attempt++) {try {// 直接尝试执行实际的请求return await requestFn();} catch (err) {// 捕获错误,记录下来lastError = err;// 判断是否为网络相关的错误const isNetworkError = axios.isAxiosError(err) && (!err.response ||  // 没有收到服务器响应err.code === 'ECONNABORTED' || // 超时err.code === 'ECONNREFUSED'); // 连接被拒绝// 如果是网络错误且还有剩余重试次数,则执行指数退避重试if (isNetworkError && attempt < maxRetries) {// 根据公式计算当前应该等待的时间const delay = baseDelay * Math.pow(2, attempt);console.log(`[Retry] Attempt ${attempt + 1}/${maxRetries} after ${delay}ms`);// 使用 Promise 和 setTimeout 实现异步延迟await new Promise(resolve => setTimeout(resolve, delay));continue; // 再次尝试进行请求}// 如果不是可重试的网络错误或已经超出最大重试次数,跳出循环并最终抛出错误break;}}// 如果所有尝试都失败,抛出最后一次的错误throw lastError;
}

核心功能拆解

1. 指数退避的实现

指数退避的核心公式是 baseDelay * Math.pow(2, attempt)。代码中:

  • baseDelay 是每次重试的基础等待时间,初始值由调用者传递(默认为 1000 毫秒)。
  • 每次重试后,等待时间按指数增加,第一次重试等待 baseDelay 毫秒,第二次等待 baseDelay * 2 毫秒,第三次等待 baseDelay * 4 毫秒。

通过 PromisesetTimeout 的结合,实现异步等待:

await new Promise(resolve => setTimeout(resolve, delay));

从而使程序在每次重试之前等待 动态时间间隔


2. 网络错误的处理逻辑

在重试机制中我们需要区分可重试错误与不可重试错误。事实上,只有网络错误才适合执行重试,例如:

  • 超时(HTTP 状态码为 408ECONNABORTED)。
  • 连接被拒绝(ECONNREFUSED)。
  • 服务器未响应(没有 response 返回)。

通过 AxiosisAxiosError 方法,代码对错误类型进行了详细判断,确保只有网络相关错误被捕获:

const isNetworkError = axios.isAxiosError(err) && (!err.response || err.code === 'ECONNABORTED' || err.code === 'ECONNREFUSED');

3. 最大重试次数的控制

为了防止无限重试引发性能问题,该实现使用了 maxRetries 参数以控制最大尝试次数。代码中明确约定了:

  • 初次尝试计为第 0 次。
  • 重试次数最多不超过 maxRetries

这里控制重试次数:

for (let attempt = 0; attempt <= maxRetries; attempt++) {

任何超出设定最大次数(如 2 次)的重试都会被终止。


使用示例

可以将这个函数应用到任何需要发送 HTTP 请求的场景中,例如从 API 获取数据:

import axios from 'axios';const client = axios.create({baseURL: 'https://api.example.com',timeout: 5000
});async function fetchData() {try {const response = await requestWithRetry(client, () => client.get('/data'), 3, 1000);console.log('Data: ', response.data);} catch (error) {console.error('Failed to fetch data:', error);}
}fetchData();

在这个例子中:

  • 我们尝试从 https://api.example.com/data 获取数据。
  • 如果请求失败,最多会重试 3 次,每次等待时间依次为 1 秒、2 秒、4 秒
  • 如果所有尝试都失败,最终会抛出错误并记录到日志。

输出示例

假设一次请求出现错误,以下是输出:

[Retry] Attempt 1/3 after 1000ms
[Retry] Attempt 2/3 after 2000ms
[Retry] Attempt 3/3 after 4000ms
Failed to fetch data: Error: ECONNABORTED

最适用的场景

  • 网络请求中容易由于瞬时错误失败,比如 API 接口短暂不可用。
  • 需要提高异步操作的鲁棒性,而不是因为一次失败就终止流程。

总结

使用 TypeScript + Axios,借助指数退避实现了自动重试的功能。该解决方案兼容性强、扩展性高,适用于任何异步任务场景。通过适当配置 maxRetriesbaseDelay,不仅可以提升操作的容错性,还能够避免在高压力环境下对系统资源的过度消耗。

如果你需要灵活的异步请求重试机制,这将是一个理想的实现。

http://www.jsqmd.com/news/385281/

相关文章:

  • 深入研究大数据领域 Hadoop 的 HBase 数据库
  • Games202(一):实时阴影(Real-time Shadows)
  • 苹果充值卡回收常见问题解答 - 京顺回收
  • Go基础:Go的函数和方法详解
  • 负债逾期别乱投医!2026最新贷款+信用卡分期协商机构口碑榜单,实测靠谱助你上岸 - 代码非世界
  • 2025金融AI智能体投资决策的落地趋势:架构师的判断与建议
  • SVG 文本:图形与文本的完美融合
  • MongoDB 更新文档
  • SQL RIGHT JOIN
  • AI原生应用领域微服务集成的服务发现机制
  • Java 发送邮件
  • JavaScript Navigator
  • C 标准库 - `<errno.h>`
  • AI辅助编程工具(八) - Cursor配合通义干问
  • 从获客到流失:完整用户生命周期价值建模方法
  • jQuery UI 如何使用部件库
  • DOM 遍历
  • zhuyifan 的考试
  • 题解:洛谷 P11641 【MX-X8-T0】「TAOI-3」分数
  • 15分钟带你搞懂AI Agent、LLM和RAG那些事儿,小学生都能看懂!!
  • 【AI大模型】Embedding技术:Sentence-BERT句嵌入模型介绍和实践
  • 深度理解RAG中的嵌入模型Embedding Model,看这一篇就够了!!
  • 大模型RAG 架构图解:从基础到高级的7种模式!
  • 完整教程:嵌入式系统(51单片机)核心外设详解:UART通信与DS18B20温度采集
  • 题解:洛谷 P9226 糖果
  • [嵌入式系统-217]:Buck 电路(降压电路)
  • Claude Code配合Astro + GitHub Pages:为 sharelatex-ce 打造现代化的开源项目宣传页
  • AI Agent在企业人才发展与继任计划制定中的应用
  • [嵌入式系统-216]:开关电源与PWM有异曲同工之妙
  • Spark大数据处理:技术、应用与性能优化【1.9】