3

I'm still figuring out reactive programming so I'm pretty sure this is very basic, but the number of stream transformations is pretty overwhelming to a beginner.

I'm creating an Observable from a DOM event. This event should in turn trigger a REST call and all other DOM events will be ignored until this event has been resolved.

const stream = Observable.fromEvent(document, 'some-event')
stream
  .flatMap(() => httpRestService())
  .subscribe(() => {
  })

How do I ignore the events from the stream until the last HTTP promise has resolved?

DOM event
A - - - - B - - - - C
HTTP event
D ...........done - C
Jack Guy
  • 8,346
  • 8
  • 55
  • 86
  • First of all, an observer does not emit values, it receives values. So here your observer is an observable. Then I don't know what you mean by 'ignored'. Are those events lost or queued? The best way to introduce your question is by specifying input and expected outputs. This is best done with a marble diagram – user3743222 May 11 '16 at 23:05
  • @user3743222 updated – Jack Guy May 11 '16 at 23:15
  • maybe `skipUntil` would be what you're looking for? http://reactivex.io/documentation/operators/skipuntil.html – Stephen Gilboy May 11 '16 at 23:26
  • I'll bite -- *why* do you want to ignore all DOM events until the last HTTP promise is resolved? Is it because you don't want to trigger additional HTTP calls until the last one is resolved? If that's the case, can you just disable inputs / show a spinner / whatever else stops the DOM events from happening in the first place instead? Otherwise, I agree with @Stephen -- `skipUntil` is probably closest to what you want. – dvlsg May 11 '16 at 23:33
  • There is also `pausable` https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/backpressure.md#pausable-observables – Stephen Gilboy May 11 '16 at 23:36
  • I fail to see how `skipUntil` will work in that particular case. The http call must also be **triggered** by the event. `skipUntil` only addresses the event skipping. Maybe a `jsfiddle` could make the case clear? Same goes for `pausable`. – user3743222 May 11 '16 at 23:45
  • `pausable` keeps a buffer though, doesn't it? I'm also not sure it's going to still be around in [RxJS 5](https://github.com/ReactiveX/rxjs/blob/master/MIGRATION.md). I'm also trying to think through using `skipUntil` and I'm having a hard time coming up with a good way. Every time you use `skipUntil` on an `Observable` it returns a new `Observable`, right? And the subscription would still be on the original `Observable` without `skipUntil` applied to it? You'd have to re-subscribe each time an `http` call came through. – dvlsg May 11 '16 at 23:46
  • Not sure `pausable` uses a buffer. If you look at the tests (https://github.com/Reactive-Extensions/RxJS/blob/master/tests/observable/pausable.js) you will see that events are lost when the stream is paused. – user3743222 May 11 '16 at 23:51

1 Answers1

6

You could try flatMapFirst which seems to do what you want. The following code could work (jsfiddle here - click anywhere) :

const stream = Observable.fromEvent(document, 'some-event')
stream
  .flatMapFirst(() => httpRestService())
  .subscribe(() => {
  })

Quoting the documentation :

The flatMapFirst operator is similar to the flatMap and concatMap methods described above, however, rather than emitting all of the items emitted by all of the Observables that the operator generates by transforming items from the source Observable, flatMapFirst instead propagates the first Observable exclusively until it completes before it begins subscribes to the next Observable. Observables that come before the current Observable completes will be dropped and will not propagate.

UPDATE

Looking at the source code (https://github.com/Reactive-Extensions/RxJS/blob/master/src/core/linq/observable/switchfirst.js) it seems that while the the current observable has not completed, all the incoming observables in the meantime will be discarded, i.e. not subscribed to.

So if subscribing to these observables triggers the http call (would be interesting to see the code for httpRestService), then there is no unnecessary http call. If those calls are triggered immediately by calling the function and the result is passed through an observable, then there is a possibility that those calls are indeed triggered unnecessarily. In which case, that issue is easily solvable with using the defer operator to do the http call only at subscription time. In short, you need lazy execution of the rest request if you don't already have it.

user3743222
  • 18,345
  • 5
  • 69
  • 75
  • I'll be happy to know whatever is wrong with the proposed solution as I observe it has been downvoted. – user3743222 May 12 '16 at 00:02
  • This is perfect. Thank you! – Jack Guy May 12 '16 at 04:11
  • 2
    Are you sure this is giving what you want? Unless I'm mistaken this will still invoke the httpRestService call for each document event, it will just throw all the results away until the previous call has returned. – Matt Burnell May 12 '16 at 04:44
  • 3
    More precisely, while the current observable has not completed, all the incoming observables in the meantime will be discarded, i.e. not subscribed to. So if subscribing to these observables triggers the http call, then there is no unnecessary http call. If those calls are triggered immediately by calling the function and it is just the result which is passed through an observable, then there is a possibility that those calls are indeed triggered unnecessarily. In which case, that issue is easily solvable with using the `defer` operator to do the http call only at subscription time. – user3743222 May 12 '16 at 06:24
  • Thank you for clarifying! After running your sample code I had a feeling this was the case, but your explanation made it perfectly clear. – Matt Burnell May 12 '16 at 07:51