I'm looking for an RxJS operator (or a combination of operators) which will allow me to achieve this:
- Every event fired by the observable should be handled by the order it was triggered (no skipping, throttling, debouncing any of the values).
- I should be abled to define a set interval between the handling of 2 subsequent events.
- In case no previous events are currently handled, the next event should be handled immediately (no delay).
I created the following example which I hope will make this clearer:
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);
}
}
In this example, I would like each value incremented in the counter to be shown as the displayedValue for at least 2 seconds.
So the first time the user clicks onClick the displayed value should immediately change from 1 to 2 but in case the user keeps on rapidly incrementing the counter the displyedValue should follow along in a delayed manner allowing each of the previously incremented numbers to be displayed for 2 seconds before changing, slowly catching up with the current counter value.
Can this be achieved with RxJs operators?
UPDATE
For the moment I came up with the following solution:
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);
}
}
}
It's not what I was looking for since it relies on the component state and on setInterval in order to manage the notification queue. I was hoping to find something cleaner and more declarative which relies on RxJs operators to handle my notifications stream, and their interval queue - but at least I got the feature to work exactly as I wanted. Still open to suggestions on how to make this better.