rxjsredux-observable

RxJS: Is there an no-op observable?


I have an action that will then trigger an ajax request.

If the action fails for some reason, I want to do nothing. Instead of creating a blank action that just returns the previous state, is there a no-op function I can execute?

export default function fetchMeetups(action$) {
  return action$.ofType(statusActions.START_APP)
    .mergeMap(action =>
      ajax.getJSON(`${config.API_BASE_URL}/api/v1/meetups`)
      .map(meetups => calendarActions.meetupsReceived(meetups))
    )
    .catch(error => Observable.noop())
};

I already have the meetups saved from the last time the app was open (using redux-persist), so if the api request fails I just want it to do nothing.

Is this possible?

I found this from Rxjs but I have no clue how to use it: https://xgrommx.github.io/rx-book/content/helpers/noop.html


Solution

  • Heads up: that link to xgrommx references RxJS v4, not v5 or v6. noop is also just a function that does nothing--not an Observable which emits nothing, which is what I believe you're looking for.

    That said, I would highly discourage against swallowing errors completely like this. It can make debugging this and other things very very hard later. I would at least log the error message out.


    v5 comes with Observable.empty() or import { empty } from 'rxjs/observable/empty'; which produces an Observable that will emit nothing and just immediately complete.

    However, there are some other subtleties you probably will run into next. If you let the ajax error propagate up to the outer operator chain, outside of the mergeMap, your Epic will not longer be listening for future actions! Instead, you'll want to catch errors as early as possible, in this case by placing the catch inside the mergeMap. We often call this "isolating our observer chains"

    export default function fetchMeetups(action$) {
      return action$.ofType(statusActions.START_APP)
        .mergeMap(action =>
          ajax.getJSON(`${config.API_BASE_URL}/api/v1/meetups`)
            .map(meetups => calendarActions.meetupsReceived(meetups))
            .catch(e => {
              console.error(e);
              return Observable.empty();
            })
        );
    };
    

    Now, whenever the ajax (or the map operation) errors, we're catching that error before it propagates out and instead switching to our empty Observable which will complete immediately so the inner chain is now "done" but our Epic will continue to listen for future actions.


    UPDATE:

    In v6 empty() is imported from the root import { empty } from 'rxjs'; or it is also available as a singleton import { EMPTY } from 'rxjs';, which can be used as-is, you don't call it like you would empty(). It can be reused because Observables are lazy and act like a factory anyway so empty() was redundant.

    import { EMPTY } from 'rxjs';
    import { catchError } from 'rxjs/operators'; 
    
    // etc
    source$.pipe(
      catchError(e => {
        console.error(e);
        return EMPTY; // it's not a function, use it as-is.
      })
    );