Sal
Sal

Reputation: 5684

RxJS add catch() to an Observable that's been shared

I have the following code I'm using in my Angular (4.0) application during $http calls.

return this.httpComponent.post(serviceUrl, request, args)
            .map((res: any) => res.json() as R)
            .catch((error: any) => Observable.throw(error.json().error || error.json().errorMessage || 'Server error'));

After testing this I realized multiple subscriptions were triggering the request multiple times. Thanks to this post: Angular2 http.post gets executed twice I found out I needed to share() the result.

This works to get rid of the multiple calls but now it seems my catch() method isn't being hit. I want my catch() to throw the error.

I tried the two options below but they did not work:

return this.httpComponent.post(serviceUrl, request, args)
    .map((res: any) => res.json() as R)
    .share()
    .catch((error: any) => Observable.throw(error.json().error || error.json().errorMessage || 'Server error'));

return this.httpComponent.post(serviceUrl, request, args)
    .map((res: any) => res.json() as R)
    .catch((error: any) => Observable.throw(error.json().error || error.json().errorMessage || 'Server error') 
    .share()); //This doesn't make sense since my catch() isn't returning an Observable

Anyone know how I can share() and catch(throw...) at the same time?

Upvotes: 1

Views: 763

Answers (2)

cartant
cartant

Reputation: 58400

The second option you've included in your question attaches the catch to the Observable.throw. If that is corrected, you should see the behaviour you are expecting:

const source = Rx.Observable
  .interval(500)
  .map((value) => {
    if (value === 2) {
      throw new Error("Boom!");
    }
    console.log(`value: ${value}`)
    return value;
  })
  .catch((error) => Rx.Observable.throw(new Error(`Re-thrown ${error.message}`)))
  .share();

source.subscribe(
  (value) => console.log(`subscription 1: ${value}`),
  (error) => console.log(`subscription 1: ${error}`)
);
source.subscribe(
  (value) => console.log(`subscription 2: ${value}`),
  (error) => console.log(`subscription 2: ${error}`)
);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>

Upvotes: 0

Sal
Sal

Reputation: 5684

As mentioned in my comment to @cartant 's answer, if any subscriber doesn't have an error handler (ie: doesn't care about any error scenarios) an exception is thrown and all subsequent subscribers are never informed of the original error.

This seems like a design flaw in my opinion. Here is the example (Copied from @cartant 's answer)

const source = Rx.Observable
  .interval(500)
  .map((value) => {
    if (value === 2) {
      throw new Error("Boom!");
    }
    console.log(`value: ${value}`)
    return value;
  })
  .catch((error) => Rx.Observable.throw(new Error(`Re-thrown ${error.message}`)))
  .share();

source.subscribe(
  (value) => console.log(`subscription 1: ${value}`)
);
source.subscribe(
  (value) => console.log(`subscription 2: ${value}`),
  (error) => console.log(`subscription 2: ${error}`)
);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script>

Upvotes: 2

Related Questions