Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concurrent Ajax requests with Rxjs

I'am currently switched from promises to observables. I am using Redux-Observable for my react app. Basically, I am looking for the best operator that will enable mutliple, concurrent ajax calls and return the responses when all the observables have sucessfully finished executing. Here is a code snippet from my app.

let epicPostAd = (action$, store, {ajax}) =>
  action$.ofType(POST_AD)
   .debounceTime(1000)
   .mergeMap(({ payload }) =>
     ajax(generateAjaxRequestSetting(POSTS_URL, 'post', payload,CT_JSON))
      .map(response => postAdSuccessful(response))
      .catch(e => Observable.of(postAdUnsuccessful(e.xhr.response)))
      .takeUntil(action$.ofType(LOCATION_CHANGE))
    )

It is a simple ajax request that posts given ad and dispatches POST_AD_SUCCESSFUL when response is 201 else dispatches POST_AD_UNSUCCESSFUL on error. But the issues is I want to make subsequent ajax observable stream when there is a response. Such as

.map(response => /* start a stream of ajax observables then process the response */)

I will appreciate if you show me the optimal way of achieving this.

like image 662
grustamli Avatar asked Mar 30 '17 01:03

grustamli


1 Answers

Sounds like you're looking for the forkJoin operator.

It will subscribe to all the Observables you pass to it and after they all complete, it will emit the last value from each inside an array.

It wasn't entirely clear where in your Epic you wanted to do this, so I just made a generic example:

const somethingEpic = (action$, store, { ajax }) =>
  action$.ofType(SOMETHING)
    .mergeMap(() =>
      Observable.forkJoin(
        ajax('/first'),
        ajax('/second'),
        ajax('/third')
      )
      .do(results => {
        // the results is an array, containing each
        const [first, second, third] = results;
        console.log(first, second, third);
      })
      .map(results => ({
        type: 'SOME_RESULTS',
        results
      }))
    );

Technically, it supports a final resultSelector argument you can use instead of using the map operator after it, but I tend not to use it because I've found it's less clear with only negligible performance benefits in common redux-observable style cases. But it's still good to know. Can be handy for more "data-normalization" stuff rather than "transform this into an action" stuff.

const somethingEpic = (action$, store, { ajax }) =>
  action$.ofType(SOMETHING)
    .mergeMap(() =>
      Observable.forkJoin(
        ajax('/first'),
        ajax('/second'),
        ajax('/third'),
        results => ({
          type: 'SOME_RESULTS',
          results
        })
      )
    );

ALSO, if you're asking yourself "what operator do I use?" you should try the operator wizard located in the documentation: http://reactivex.io/rxjs/

Scroll down to the part that says:

Do you need to find an operator for your problem? Start by choosing an option from the list below:

  • I have one existing Observable, and...
  • I have some Observables to combine together as one Observable, and...
  • I have no Observables yet, and...

Hint: open your DevTools to experiment with RxJS.

Though in this case, forkJoin is correctly suggested but when you click on it, it isn't yet documented :sadface: But a google search would present many different websites explaining what it does and how to use it (in RxJS and in other Rx implementations in other languages). Like this helpful website

like image 157
jayphelps Avatar answered Sep 22 '22 13:09

jayphelps