头图

First understand the role of the combineLatest operator:

Combines multiple Observables to create an Observable whose value is computed from the latest value of each of its input Observables.

Its marble diagram is shown in the following figure:

We have a stream of limit values and a stream of offset values. We combine these streams using combineLatest to create a stream that will have a new value every time one of the source streams changes. Then we use switchMap to fetch data from the backend to get pokemon$ based on these values. Because we use switchMap, if a call has not finished, then when a new call initiates a new call by changing limit or offset, the previous call will be canceled.

code show as below:

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),
      );

The code address is as follows:

https://stackblitz.com/edit/angular-deqtkx

When I change the limit and offset to other values and click the reset button, I will observe that two requests are initiated successively, and the first request is automatically canceled:

By clicking the reset button, we update our two source streams by resetting the limit and offset values at the same time. The effect of this action is that the stream created by combineLatest is fired twice, thus starting two backend requests, and on the other hand, canceling one immediately since we used switchMap.

Let's take a step-by-step disassembly to deepen the impression:

  • combineLatest holds the last value of all source streams. For example, the starting scene is, limit = 8, offset = 2)
  • Click the reset button
  • limit is set to 5
  • combineLatest sees a new value going into limit and emits a new combination, limit=5, offset=2
  • switchMap takes these values and subscribes to the stream that triggers the backend call
    offset is set to 0
  • combineLatest sees a new offset value, and emits a new combination, limit=5, offset=0
  • switchMap takes these values, unsubscribes (and thus cancels) the previous request and starts a new one

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

How to avoid this behavior

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

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

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

Whenever you refer to "in the same call stack", you can replace it with "changes that occurred in the same round of the event loop".

Timing diagram after adding debounceTime(0):

  • combineLatest saves the last value of all source streams, starting scenario is, limit = 8, offset = 2
  • Click the reset button
  • Limit is set to 5
  • combineLatest operator sees a new value into limit and emits a new combination, limit=5, offset=2
  • The debounceTime operator sees a new value and (because the operator's value is 0) will wait until the call stack is cleared to pass it
  • offset is set to 0
  • The combineLatest operator sees a new offset value and emits a new combination with 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
  • call stack is cleared
  • The debounceTime operator doesn't see the new value and will send data downstream by combining, limit=5, offset=0
  • The switchMap operator takes these values and subscribes to the stream that triggers the backend call

Fixing the code is as simple as adding a single line of code:

debounceTime(0),

After the repair, after clicking the reset button, only one HTTP request is made:

More Jerry's original articles, all in: "Wang Zixi":


注销
1k 声望1.6k 粉丝

invalid