Filter Observable by Observable

208 views Asked by At

I want to make sure that Observable.subscribe() doesn't get executed if a different Observable yields true.

An example use case would be making sure that user can trigger a download only if the previous one has finished (or failed) and there's only one download request executed at a time.

In order to control the execution flow, I had to rely on a state variable which seems a bit odd to me - is this a good pattern? In a v. likely case that it isn't - what would be a better approach?

I ended up with two subscriptions: Actions.sync (using a Subject, public API, initialises a sync request) and isActive (resolves to true or `false, the name should be pretty self-explanatory.

let canDownload = true; // this one feels really, really naughty
const startedSyncRequests = new Rx.Subject();
const isActiveSync = startedSyncRequests.map(true)
  .merge(completeSyncRequests.map(false))
  .merge(failedSyncRequests.map(false))
  .startWith(false)
  .subscribe(isActive => canDownload = !isActive)

syncResources = ()=>{
  startedSyncRequests.onNext();
  // Mocked async job
  setTimeout(()=> {
    completeSyncRequests.onNext();
  }, 1000);
};


Actions.sync
  .filter( ()=> canDownload ) // so does this
  .subscribe( syncResources );
1

There are 1 answers

2
paulpdaniels On

You want exclusive().

Actions.sync
  .map(() => {
    //Return a promise or observable
    return Rx.Observable.defer(() => makeAsyncRequest());
  })
  .exclusive()
  .subscribe(processResults);

The above will generate an observable every time the user makes a request. However, exclusive will drop any observables that come in before the previous one has completed, and then flattens the resulting messages into a single observable.

Working example (using interval and delay):

var interval = Rx.Observable.interval(1000).take(20);


interval
  .map(function(i) {
    return Rx.Observable.return(i).delay(1500);
  })
  .exclusive()
  //Only prints every other item because of the overlap
  .subscribe(function(i) {
    var item = $('<li>' + i + '</li>');
    $('#thelist').append(item);
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.3/rx.all.js"></script>
<div>
  <ul id="thelist">
  </ul>

</div>

Reference: here