我一直在try 实现我自己的RxJS操作符,这样我就可以将它"插入"到我的Angular 的HttpClient.我希望保持请求每X毫秒进行一次(轮询),但也希望在出现错误的情况下使用某种增量策略重试请求.以下是一个细目:

  1. 每X毫秒重复一次请求(轮询)
  2. 如果出现错误,应每隔Y毫秒重试一次请求,但如果请求再次失败,则毫秒数应加倍(例如,第一次失败1000ms,第二次失败2000ms,第三次失败4000ms),直到达到限制,如果达到该限制,例如1分钟,则重试不会再次增加,所有后续重试都应等待1分钟.
  3. 成功重试后,重试毫秒计数器应重新启动,以便下一次它将从Y毫秒开始重试.
  4. 当执行一个请求时,无论出于何种原因,都需要花费大量时间来响应,我们需要确保不会触发或取消任何其他请求(我可以在这里看到linatMap的可能用法!)

以下是我目前掌握的情况:

function repeatWithBackoff<T>(delay: number, maxDelay = 60000) {
  return (source: Observable<T>) =>
    timer(0, delay).pipe(
      concatMap(() => {
        return source.pipe(
          retryWhen((attempts) => {
            return attempts.pipe(
              concatMap((attempt, i) => {
                const backoffDelay = Math.min(delay * Math.pow(2, i), maxDelay);
                return timer(backoffDelay);
              })
            );
          })
        );
      })
    );
}

以下是我使用它的方法:

httpClient.post(...)
  .pipe(repeatWithBackoff(1000, 60000))
  .subscribe((x) => console.log('Result', x));

显然,我的代码是kinda works,但重试不能正常工作,我预计请求失败时不会发生任何事情(例如,no console.log()),但我可以看到可观察到的数据打印到控制台. 此外,如果有人有一个巧妙的主意来简化该功能,我将不胜感激:)

推荐答案

现代RxJS通过使用内置的retryrepeat运算符简化了这项任务,这些运算符接受非常强大的配置.

import { pipe, retry, repeat, timer } from 'rxjs'

function pollWithBackoff<T> (delay: number, maxDelay: number): MonoTypeOperatorFunction<T> {
  return pipe(
    retry({
      delay: (_error, i) => {const backoffDelay = Math.min(delay * Math.pow(2, i-1), maxDelay);
      return timer(backoffDelay);}
    }),
    repeat({
      delay
    }),
  )
}

action on stackblitz分钟后见.

创建复运算符的最佳方法是使用pipe函数.这使您能够按照在.pipe方法中使用RxJS运算符的方式来组合它们.不需要处理源可观测对象,您需要处理其中的所有错误和完成并将它们发送到输出可观测对象.

首先,使用接受配置对象的retry运算符,在其中可以提供定制的计时函数.从retry操作符开始是很重要的,因为我们希望在每次请求出错时重新启动回退.Note: I don't know why, but it appears that the error index in 102 starts with 1.

现在,您有了"接受此请求并使用指数回退重试"的逻辑.之后,添加repeat运算符,该运算符再次接受带有delay的配置对象.这确保了前面的逻辑重复.因为我们已经处理了重试,所以使用repeat运算符仍然很简单,并且只在成功发出请求后才重复.

有关配置对象的更多信息,请参见retry documentation,repeat documentation.

Angular相关问答推荐

根据Angular中的构建来卸载独立组件

如何在Angular中执行一个操作条件为多个可观测值?

如何在init为FALSE时以Angular 初始化主拇指擦除器-Swiper 11.0.6Angular 17 SSR

Angular 环境本地无文件替换

如何使用解析器处理Angular 路由中存储的查询参数以避免任何额外的导航?

如何将Angular 服务包含在延迟加载的独立组件路由中?

Angel Project Build从Angel 8升级后,从12MB增加到90MB

如何从2个api获取数据并查看Angular material 表中的数据?

NX如何在前端和后端使用一个接口库

对象中的值丢失 angular ngrx

最新版本的 Chrome ERR_TIMED_OUT

全局异常处理

单选按钮的Angular2 Reactive Forms formControl

运行 npm 测试(Angular 2 单元测试)后无法读取未定义的属性 subscribe

如何在angular 5 中获取基本网址?

如何在 Angular 4 中使用 Material Design 图标?

为什么我们需要`ngDoCheck`

*ngIf 带有多个异步管道变量

如何在 Angular 4 中翻译 mat-paginator?

Angular CLI 为已经存在的组件创建 .spec 文件