0

I have Angular input where every time its value changes I ask service to get some data about entered value. I only care about last entered input, so when user enters '1', but then erases it and enters '2' I don't care about data about previous value. I wrote something like this (I keep value of previous request in prevNr):

let stream,
  nr = document.querySelector('input').value;

if (status === `${nr} in progress`) {
  stream.unsubscribe();
  console.log(`subscription of ${prevNr} cancelled`)
}

status = 'in progress';
prevNr = nr;

console.log(`started subscription of ${nr}`)
stream = someService.someMethod.subscribe(() => {
  const clonedNr = nr;
  setTimeout(() => {
    status = 'done';
    console.log(`received response for ${clonedNr}`)
  }, 12000);
})

What I get back in console is

1 in progress

subscription of 1 cancelled
2 in progress

subscription of 2 cancelled
3 in progress

subscription of 3 cancelled
4 in progress

received response for 1
received response for 2
received response for 3
received response for 4

Right now I mock response by setTimeout(), but I can imagine situation where I receive data for input 4 before data for input 3 and as result this data will be assigned to wrong input. How can I omit previous requests in Observable? Is that possible?

Jarosław Rewers
  • 1,059
  • 3
  • 14
  • 23
  • The third callback function in subscribe is for [completed](http://reactivex.io/rxjs/class/es6/Subscriber.js~Subscriber.html) – Dhyey Jun 26 '17 at 10:41

1 Answers1

0

A few notes:

You probably want to add a debounce to the nr stream. That way if the user types multiple numbers in rapid successsion, you don't send a request for each number. debounceTime() lets you wait for a set amount for milliseconds after a value is entered, if no new value is entered in that time, it's passed on. If a new value is entered within the set time, the time will be reset; rinse and repeat.

You should not do async work in the subscribe(), it "kind of" breaks Rx. All async work should be done before subscribe() inside another operator; very often one the *Map() operators.


I'm assuming someMethod() returns an observable (or you can probably convert it to one) and in that case you'll want the switchMap() operator. You return an observable inside switchMap() and it will only subscribe to the most recent observable. It will also unsubscribe from the previous observable.

let stream,
nr = document.querySelector('input').value
    .debounceTime(500); // This will wait 500 ms before passing on a value
                        // If a new value arrives before 500ms has passed, it resets the time

stream = nr
    .switchMap(num => someService.someMethod(num))
    .subscribe(clonedNr => console.log(`received response for ${clonedNr}`));
Jon G Stødle
  • 3,844
  • 1
  • 16
  • 22