1

Consider following example:


import { fromEvent } from 'rxjs'; 
import { switchMap, tap } from 'rxjs/operators';
import { ajax } from 'rxjs/ajax';

const httpCall$ = ajax.getJSON('https://rickandmortyapi.com/api/character/');
const click$ = fromEvent(document, 'click');

const switchMapExample$ = click$.pipe(
  tap(() => console.log('inside switchMap - click happend')),
  switchMap(() => {
    console.log('inside switchMap - start http request');
    return httpCall$.pipe(
        tap((val) => console.log('inside switchMap - http response ', val))
    );
  }
));

switchMapExample$.subscribe((val) => {
  console.table(val); // Is There a way to log only the latest value ?
}); 

By clicking inside the document it's process a new request.

See the Blitz here: rxjs-about-switchmap

Using SwitchMap allow to cancel previous request. How can i subscribe only to the latest Request reponse ?

billyjov
  • 2,778
  • 19
  • 35
  • aren't the code already giving you the latest request response? – Fan Cheung Nov 06 '19 at 11:07
  • In this case yes...but if the request take too long time or if the user click too fast it's emit the other values ..But i only want the latest completed request value – billyjov Nov 06 '19 at 11:13
  • what is the goal? getting the latest response or just make sure that a fast clicking user is not spamming http requests? – Arikael Nov 06 '19 at 12:41
  • getting the latest response... It's ok that every click send a new request... using switchMap allow me to cancel earlier request..but want to subscribe only to the last completed request. – billyjov Nov 06 '19 at 12:48
  • in your particular case `switchMap` doesn't really have an effect because the requests complete so fast. Every request has completed before the new request is made. You can check that by using `finalize` and output something or by using `delay` to simulate latency. You could mitigate that by using `debounceTime` for your click event, don't know if that helps in your case – Arikael Nov 06 '19 at 13:19
  • 1
    this is weird requirement, if it is a pending request and you cancel it and replace with the last result, how is previous result being the latest result, coz you stopped the latest result from retrieving. If the result always takes longer than click, you always have the old result. – Fan Cheung Nov 06 '19 at 15:04

3 Answers3

1

You can use the shareReplay() operator of the RxJS. Check out the solution https://stackblitz.com/edit/rxjs-about-switchmap-9blsbq.

import { fromEvent } from "rxjs";
import { switchMap, tap, shareReplay } from "rxjs/operators";
import { ajax } from "rxjs/ajax";

const httpCall$ = ajax
  .getJSON("https://rickandmortyapi.com/api/character/")
  .pipe(
    tap(() => console.log("http request")),
    shareReplay(1),
    tap(() => console.log("http response"))
  );

const click$ = fromEvent(document, "click").pipe(
  tap(() => console.log("click happend"))
);

const switchMapExample$ = click$.pipe(switchMap(() => httpCall$));

switchMapExample$.subscribe(val => {
  console.log(val);
});
  • Thanks for your response... `last()` cannot fit here because it will emit only the last value ..But consider the user click too fast ..it wil only emit the initial last value – billyjov Nov 06 '19 at 12:27
  • In that case, you could use `shareReplay()`, check the solution https://stackblitz.com/edit/rxjs-about-switchmap-9blsbq – Ramesh Thiruchelvam Nov 06 '19 at 14:43
0

Seems like a job for exhaustMap operator

It is somehow opposite of switchMap:

If switchMap is used, pending backend requests are aborted in favour of more recently dispatched actions. However, if exhaustMap is used, dispatched actions are ignored whilst there is a pending backend request.

There also concatMap and mergeMap operators which might work for you. In order to understand difference between them take a look at this awesome article:

Oles Savluk
  • 4,315
  • 1
  • 26
  • 40
0

When is the latest request?

At what point in time would you define what the latest request is?
You would need to collect every click the user does and at some other point execute the request. but that seems rather weird

Every http request observable will complete once the request itself has completed. If you don't have any overlapping requests switchMap won't do anything because there isn't anything to switch.
You can verify that if you add a delay

see https://stackblitz.com/edit/rxjs-about-switchmap-jhy3v4
If you add a delay to your request to simulate latency you get the result you would expect from switchMap

Without the delay your request is so fast it has completed as soon as you click again and there is nothing to switch because the inner observable has already completed hence you get all the results because it's a new stream of events.
This is also why for example last or takeLatest won't help here.

Here's what I would to
https://stackblitz.com/edit/rxjs-about-switchmap-jhy3v4

In the button with id test I would use debounceTime in combination with switchMap and enabling/disabling the button

const click3$ = fromEvent(document.querySelector('#test'), 'click');

switchMapExample2$.subscribe((val) => {
  console.table(val);
});

const button = document.querySelector('#test');

const switchMapExample3$ = click3$.pipe(
  tap(() => button.setAttribute('disabled', 'disabled')),
  debounceTime(500),
  tap(() => console.log('inside switchMap - click happend')),
  switchMap(() => {
    console.log('inside switchMap - start http request');
    return httpCall$.pipe(
        tap((val) => console.log('inside switchMap - http response ', val))
    );
  }));

switchMapExample3$.subscribe((val) => {
  console.table(val);
  button.removeAttribute('disabled');
});

Just be aware of RxJS: Avoiding switchMap-Related Bugs posted by Oles Savluk.

Arikael
  • 1,989
  • 1
  • 23
  • 60