RxJS: Is there an no-op observable?
Asked Answered
S

2

7

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

Simulation answered 18/4, 2017 at 14:21 Comment(0)
D
12

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.
  })
);
Duero answered 18/4, 2017 at 17:35 Comment(2)
Thanks for details! One question related to this. I faced an issue where returning Observable.empty() within an async function was return following error "actions must be plain objects. use custom middleware for async actions". Do you have an idea on how the syntax should be in case of async function return empty()?Predicative
Careful if you ever use forkJoin() with EMPTY observables, for instance if you had several HTTP calls you might use forkJoin to wait for all to complete. It will short-circuit and immediately complete. If you need that that you'd probably need to just return the response wrapped in a simple structure, or return a null.Own
A
0

As rxjs also accepts arrays, you can simple provide an empty array when you don't want to emit anything

...
.catch(error => return [];)
Allista answered 17/8, 2018 at 12:19 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.