簡體   English   中英

RxJS Operator 延遲處理后續事件

[英]RxJS Operator to delay handling subsequent events

我正在尋找一個 RxJS 運算符(或運算符的組合),它可以讓我實現這一點:

  1. 由 observable 觸發的每個事件都應該按照它被觸發的順序進行處理(沒有跳過、節流、去抖動任何值)。
  2. 我應該能夠定義處理 2 個后續事件之間的設定間隔。
  3. 如果當前沒有處理先前的事件,則應立即處理下一個事件(無延遲)。

我創建了以下示例,希望能更清楚地說明這一點:

import { Component, OnInit } from "@angular/core";
import { Subject } from "rxjs";
import { delay } from "rxjs/operators";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  counter = 1;
  displayedValue = 1;

  valueEmitter = new Subject<number>();
  valueEmitted$ = this.valueEmitter.asObservable();

  ngOnInit() {
    // I want to allow each incremented value to be displayed for at least 2 seconds
    this.valueEmitted$.pipe(delay(2000)).subscribe((value) => {
      this.displayedValue = value;
    });
  }

  onClick() {
    const nextCounter = ++this.counter;
    this.counter = nextCounter;
    this.valueEmitter.next(nextCounter);
  }
}

在此示例中,我希望counter遞增的每個值displayedValue為 displayValue 至少 2 秒。

因此,用戶第一次單擊onClick ,顯示的值應立即從 1 更改為 2,但如果用戶繼續快速遞增counterdisplyedValue應以延遲的方式跟隨,從而允許每個先前遞增的數字顯示為 2秒前變化,慢慢追上當前計數器值。

這可以通過 RxJs 操作符來實現嗎?

更新

目前我想出了以下解決方案

import { Component, OnInit } from "@angular/core";
import { Subject } from "rxjs";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  counter = 1;
  displayedValue = 1;
  currentDisplayedValueStartTime = 0;
  readonly DISPLAY_DURATION = 2000;
  valuesToDisplay = [];
  displayedValueSwitcherInterval: number | null = null;

  valueEmitter = new Subject<number>();
  valueEmitted$ = this.valueEmitter.asObservable();

  ngOnInit() {
    this.valueEmitted$.pipe().subscribe((value) => {
      this.handleValueDisplay(value);
    });
  }

  onClick() {
    const nextCounter = ++this.counter;
    this.counter = nextCounter;
    this.valueEmitter.next(nextCounter);
  }

  handleValueDisplay(value) {
    this.valuesToDisplay.push(value);

    if (!this.displayedValueSwitcherInterval) {
      this.displayedValue = this.valuesToDisplay.shift();

      this.displayedValueSwitcherInterval = window.setInterval(() => {
        const nextDiplayedValue = this.valuesToDisplay.shift();
        if (nextDiplayedValue) {
          this.displayedValue = nextDiplayedValue;
        } else {
          clearInterval(this.displayedValueSwitcherInterval);
          this.displayedValueSwitcherInterval = null;
        }
      }, this.DISPLAY_DURATION);
    }
  }
}

這不是我想要的,因為它依賴於組件狀態和setInterval來管理通知隊列。 我希望找到一些更簡潔、更具聲明性的東西,它依賴於 RxJs 操作符來處理我的通知流和它們的間隔隊列——但至少我讓這個功能完全按照我的意願工作。 仍然願意接受有關如何改進的建議。

所以你的評論讓它更清楚一點。

顯然,你需要一些狀態。 因為您不能一次顯示所有內容,所以用戶的屏幕不是無限的。 它不能同時處理超過.. 3.. 4.. 5 個通知。

scan是您在發出每個事件的rxjs處理狀態的方式。

您的scan可能包含兩個列表:一個用於當前呈現的通知,另一個用於待處理的通知。 列表和scan一樣保留順序,因此不會違反順序。

然后,您需要能夠根據您的事件采取不同的行動。 我能想象到的最簡單的事情是元組(event_type_as_sting_or_number, event_specific_params) 有了它,您將能夠正確計算下一個狀態(將由scan發出),然后優雅地處理它(如重新渲染通知)。

似乎你需要三個事件:

  1. 新的事件來自外太空。
  2. 某些通知已過期,必須從屏幕上移除。
  3. 用戶確認了一些通知,保留它沒有任何意義。

所以整個管道是: merge所有事件merge到一個流中,讓它們通過event_type “標記”; 使用[], []作為初始狀態scan該流(尚無待處理或活動通知); if else if else基於您在scanevent_type以正確計算下一個狀態; 獲取結果並呈現所有活動通知。


來自評論:

我仍然沒有看到我如何管理此解決方案中的間隔。 我的意思是,如何僅在顯示當前活動通知的設置間隔之后將通知從待處理數組移動到活動數組。

基本上,兩種選擇。 由於您沒有嚴格限制顯示某些通知的給定限制,因此您可以簡單地擁有發出的全局interval ,比如說,每個your_limit / 2時間單位。 當您將每個通知移至活動通知列表時,您可能會使用簡單的時間戳對其進行擴展。 下次間隔觸發時,您遍歷該列表並過濾掉時間增量>= your_limit所有通知。 這有點不准確,但可以輕松配置和調整為對您的用戶透明。 我投票支持這個解決方案,因為它很簡單。

另一種解決方案是在每次放置在活動列表上的通知中觸發timer 計時器完成后,您發送一個事件(notification_must_die, { notification_id: ... })並且您的scan處理該事件; 必須准備好這樣的notification_id不再存在(例如,它可以在之前被取消)。 這很復雜,但要准確得多。

我認為這可能是一種解決方法:

this.valueEmitted$
  .pipe(
    concatMap(
      (v) => concat(
        of(v),
        timer(2000).pipe(ignoreElements())
      )
    )
  )
  .subscribe((value) => {
    this.displayedValue = value;
  });

因為timer也發出一些值(0、1、2 等等),我們使用ignoreElements因為我們對這些值不感興趣,我們想要的所有timer完整的通知,它將指示下一個內部 observable 可以的時刻訂閱。 請注意,前面提到的內部 observable 是通過以v作為參數調用concatMap的回調函數來創建的。

工作演示。


由 observable 觸發的每個事件都應該按照它被觸發的順序進行處理(沒有跳過、節流、去抖動任何值)。

這是在concatMap的幫助下concatMap

我應該能夠定義處理 2 個后續事件之間的設定間隔。

我們可以通過以下方式實現:

concat(
  // First, we send the value.
  of(v),
  // Then we set up a timer so that in case a new notification is emitted, it will have to
  // wait for the timer to end.
  timer(2000).pipe(ignoreElements())
)

如果當前沒有處理先前的事件,則應立即處理下一個事件(無延遲)。

如果沒有由concatMap創建的活動內部 observables,則值v將立即發送給消費者。


這也可以:

this.valueEmitted$
  .pipe(
    concatMap(
      (v) => timer(2000).pipe(
        ignoreElements(), startWith(v)
      )
    )
  )
  .subscribe((value) => {
    this.displayedValue = value;
  });

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM