【问题标题】:How would I implement "lazy execution timer" using RXJS我将如何使用 RXJS 实现“延迟执行计时器”
【发布时间】:2020-11-14 19:54:39
【问题描述】:

我有一个用例,由于模板问题,Angular 的错误处理程序被 25 多条错误消息轰炸。由于我的自定义错误处理程序正在使用远程记录器 (sentry.io),因此应用程序因对 sentry.io 的 API 请求过多而无响应。

现在我使用“懒惰的计时器”方法解决了这个问题,如下所示:

import { ErrorHandler, Injectable } from '@angular/core';
import { LoggingService } from './sentry.provider';

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {

    private errorBuffer: Array<any>;
    private lazyTimer: any;

    constructor(private logger: LoggingService) {

        this.errorBuffer = [];

    };

    handleError(error) {

        this.errorBuffer.push(error);
        this.processErrorBuffer();

    };

    private processErrorBuffer() {

        if (this.lazyTimer) { clearTimeout(this.lazyTimer) };

        this.lazyTimer = setTimeout(() => {

            if (this.errorBuffer.length === 1) {

                let error = this.errorBuffer.pop();
                this.logger.reportError(error);

            } else if (this.errorBuffer.length > 1) {

                this.logger.reportError(this.errorBuffer);
                this.errorBuffer = [];

            };

        }, 300)
        
    };

}

所以基本上在每个错误之后而不是处理它我 setTimeout 它将执行错误处理逻辑或将被清除和更新(如果新错误在 300 毫秒内出现)。

这样我可以将单个错误发送到 Sentry 或批处理(数组)。

我尝试使用 RXJS 实现相同的逻辑,但未能找到方法。 我似乎不明白是哪个运营商执行这种定时器的更新。

那么我需要使用哪个运算符来使用 RXJS 复制此行为?

【问题讨论】:

  • 您熟悉debounceTime 运算符吗?你也可以这样做。
  • 是的,我查看了 debounceTime,但这只是给出了最后一个发出的值?我仍然需要捕获所有错误,而不仅仅是最后一个。至少看不到我如何在这种情况下使用 debounce 来处理发射值的爆发。或者您的意思是将所有错误存储在一个数组中并且只发出整个数组?
  • 啊!您可以使用 debouceTime 和 ReplaySubject。有意义吗?
  • ReplaySubject 取最后编辑值的数量。让我研究一下,也许我可以找到一些东西来获取所有发出的值而不是最后一个。
  • 是的,理想情况下,自上次去抖动“事件”以来发出的所有内容都是完美的。

标签: angular rxjs settimeout


【解决方案1】:

DebounceTime 描述(来自 RXJS 文档):

https://rxjs-dev.firebaseapp.com/api/operators/debounceTime

仅在特定时间后从源 Observable 发出一个值 span 已经过去,没有其他源排放。

我怎么看,在调用 this.logger.reportError 函数后,您正在删除所有错误,因此无需使用 ReplaySubject。

取决于你的功能逻辑我是这样做的。请检查示例。

import { ErrorHandler, Injectable } from '@angular/core';
import { LoggingService } from './sentry.provider';
import { debounceTime, scan } from 'rxjs/operators'

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {

    private errorListener = new Subject();   // <-- you need to import subject from RXJS

    constructor(private logger: LoggingService) {
        this.errorListener
           .pipe(
              // scan operator will collect all of the responses in one array 
              scan((acc, curr) => [...acc, curr], []), 
              // debounceTime operator will trigger event for the listeners only once when after the last action passed 400 ms 
              debounceTime(400)
            ).subscribe(errorBuffer => { 
                // errorBuffer is the collected errors list
                this.processErrorBuffer(errorBuffer);
            })
    };

    handleError(error) {
        this.errorListener.next(error);
    };

    private processErrorBuffer(errorBuffer) {
      if (errorBuffer.length === 1) {

          this.logger.reportError(errorBuffer[0]);
       } else if (this.errorBuffer.length > 1) {

           this.logger.reportError(errorBuffer);
       };
    };

}

【讨论】:

  • 让我明天彻底测试一下。我认为这是可行的,因此在这种情况下,无论输入的错误数量如何,计时器每 300 毫秒启动一次,并且由于我们将它们全部放入一个数组中,因此我们在技术上不会丢失错误。让我测试一下并确认。我想知道是否存在可以在没有数组的情况下执行相同操作的运算符组合。我查看了 bufferCount 但仍然不确定我将如何实现它。
  • @SergeyRudenko 是的,在你的测试后告诉我,我会尝试找到一种方法对 ReplaySubject 做同样的事情。如果我们能弄清楚这一点,那就太好了。我没有使用过 ReplaySubject,所以这个解决方案对我来说也很有趣。
  • 完美,刚刚测试过,效果很好!太感谢了。今天学到了一些东西:)
猜你喜欢
  • 1970-01-01
  • 2011-05-18
  • 1970-01-01
  • 2020-06-25
  • 2019-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-24
相关资源
最近更新 更多