“This is the seventh day of my participation in the First Challenge 2022.

Let’s start with the combineLatest operator:

Combine multiple Observables to create an Observable whose value is calculated from the latest value of each input Observable.

Its marbles are shown in the figure below:

We have a limiting value stream and an offset value stream. We use combineLatest to combine these flows to create a flow that will have a new value each time one of the source streams changes. We then use switchMap to get data from the back end based on these values to get Pokemon $. Because we are using switchMap, if a call has not yet finished, the previous call will be cancelled when a new call initiates a new call by changing limit or offset.

The code is as follows:

this.pokemon$ = combineLatest(limit$, offset$)
       .pipe(
        map(data= > ({limit: data[0].offset: data[1]})),
        switchMap(data= > this.pokemonService.getPokemon(data.limit, data.offset)),
        map((response: {results: Pokemon[]}) = > response.results),
      );
Copy the code

The code address is as follows:

Stackblitz.com/edit/angula…

When I set limit and offset to other values and click the Reset button, I will see that two requests are initiated and the first request is automatically cancelled:

By clicking the reset button, we update our two streams by resetting both limits and offsets. The effect of this action is that the flow created by combineLatest fires twice, thus launching two back-end requests, and, on the other hand, cancelling one immediately since we used switchMap.

Let’s take it apart in one step to deepen the impression:

  • CombineLatest holds the last value of all source streams. For example, start scene is limit = 8, offset = 2)
  • Click the Reset button
  • Limit is set to 5
  • CombineLatest sees a new value enter limit and emits a new combination, limit = 5, offset = 2
  • SwitchMap takes these values and subscribes to the flow that triggers the back-end call

Set the offset to 0

  • CombineLatest sees a new offset value and emits a new combination, limit = 5, offset = 0
  • SwitchMap takes these values, unsubscribes (and therefore cancels) the previous request, and starts the new one

What you might not expect in this process is that whenever limit is set, the change is propagated directly to combineLatest before offset is changed.

How can you avoid this behavior

If there was a way to ensure that changes made in the same call stack (which is what happens when the reset button is clicked) were discarded in favor of the last change, we could solve our problem.

This means that when combineLatest issues two values in the same call stack, the last value will be sent when the call stack is cleared.

To do this, we can use debounceTime with a value of 0 directly after combineLatest. This ensures that only the last value is passed to the switchMap, and after the call stack has been cleared.

Whenever “in the same call stack” is mentioned, it can be replaced with “changes that occurred in the same round of the event loop.”

Sequence diagram after debounceTime(0) :

  • CombineLatest holds the last value of all streams, starting with the scenario limit = 8, offset = 2
  • Click the Reset button
  • Limit set to 5
  • The combineLatest operator sees a new value enter limit and emits a new combination, limit = 5, offset = 2
  • The debounceTime operator sees a new value and (because the operator has a value of 0) will wait until the call stack is cleared to pass it on
  • Set the offset to 0
  • The combineLatest operator sees a new offset value and emits a new combination, limit = 5, offset = 0
  • The debounceTime operator sees a new value again, discards the old value and waits for the stack to be cleared to pass it on
  • The call stack is cleared
  • The debounceTime operator sees no new value and will send data downstream by combining limit = 5 and offset = 0
  • The switchMap operator takes these values and subscribes to the flow that triggers the back-end call

The fix is as simple as adding one line:

debounceTime(0),

After the reset button is clicked, only one HTTP request is issued: