Given:
An NgRx effect that handles a request to "zoom in" a display. It gets a notification every time users click on an appropriate button.
public readonly zoomIn$ = createEffect(
() =>
this.actions$.pipe(
ofType(zoomIn),
tap(() => {
scale();
}),
),
{ dispatch: false },
);
Note: The zoomIn action couldn't and doesn't contain any payload. Consider it only as a trigger
Problem:
The redrawing costs resources and in some cases occupy a few seconds to get a new scale. So if you want to scale up several times in a row you'll be compelled to wait.
Solution:
By using the debounceTime operator postpone the call of the scale() function and wait until users make several clicks. Sounds good. The only problem is debounceTime notifying us of the latest value. And what we need is a number of values (user's clicks) silenced by the debounceTime operator.
In a more general view, the task sounds like: how to calculate the count of values emitted by the source stream and silenced by the debounceTime operator?
My solution is to create a custom pipable operator that achieves the aim.
What do we need? Of course, we need a
debounceTimeoperator.Then we should calculate the number of values has been emitted. There is a
scanoperator that pretty much looks like a well-knownreducefunction. We'll give it an initial value and will increase a counter on every received value from the source stream. We have to place it before thedebounceTimeoperator. It looks now like a stream of indices.When
debounceTimenotifies us of the latest index, how can we know the number of muted values? We have to compare it with the previous index that has been emitted. The previous value can be received by using apairwiseoperator. And then get a difference between them using themapoperator.If you try this in the current state you notice that something is wrong, that it doesn't work for the first time. The problem lies in the
pairwiseoperator. It emits pairs of values (previous and current), so it waits until it has at least two values before starting the emission of pairs. Is it fair? Yes, it is? That's why we need to cheat it a little and provide a first value (that is 0), with the use of thestartWithoperator.The final implementation
Usage example