2
votes

With promises, we can use a variant of .then to split up the chain when an error occurs. Here is an example using fetch

fetch('http://website.com').then(
  // Perform some logic
  (response) => response.json().then(({ answer }) => `Your answer: ${answer}`),
  // Skip json parsing when an error occurs
  (error) => 'An error occurred :(',
).then(console.log);

This allows me to skip the response processing logic and only respond to errors that were raised in the original fetch statement. Something similar in RxJS might look like this:

Observable.fromPromise(fetch('http://website.com'))
  // if I put .catch here, the result will be piped into flatMap and map
  .flatMap(response => response.json())
  .map(({ answer }) => `Your answer: ${answer}`)
  // if I put .catch here, errors thrown in flatMap and map will also be caught
  .subscribe(console.log);

As the comments in the code state, I can't simply put a catch operator in as it doesn't have the same behaviour as my promise chain.

I know I can get it with custom operators involving materialising, or merging an error catching observable with this one, but it all seems like pretty major overkill. Is there a simple way to achieve the promise chain behaviour?

1

1 Answers

6
votes

Actually, if I was in your situation I wouldn't be worried about catching errors from flatMap and map. When the source Observable throws an error then it'll be propagated to the observer. So I'd just use an error handler when calling subscribe (otherwise the error is rethrown):

.subscribe(console.log, err => console.log('error:', err));

Note that when an error occurs in the source Observable (a Promise in your case) that it's propagated as error notifications, not as standard next notifications. This means that flatMap() and map() won't have absolutely any effect on the error message. If you used catch() or materialize() that both operators (flatMap and map) would have to be able to handle this type of data (and not throw yet another error).

Anyway you can always use share() or publish() and make two different subscriptions where each handles only one type of signals:

let source = Observable.fromPromise(fetch('http://website.com')).publish();

source
  .subscribe(undefined, err => console.log(err));

source
  .flatMap(...)
  .map(...)
  .subscribe(console.log, () => {});

source.connect();

Now I have a separate observer only for errors.

Note that I had to make an empty callback with () => {} so the error will be silently ignored. Also note that when using multicasting (the publish() operator) then the Subject inside might have some specific behavior I should be aware of but maybe it doesn't matter in your use-case.