2
votes

I'm zipping a three observables, each of the three observables has it's own "success" callback using .pipe(tap() => {...});. This works fine when all three observables execute without error, but if one of the observables errors out, then none of the tap methods execute. How can I have the tap methods always execute if that observable runs successfully?

var request1 = Observable.create(...);  //Pretend this one will fail (though request2 or request3 could also fail)
var request2 = Observable.create(...);
var request3 = Observable.create(...);

request1.pipe(tap(() => {
    //Unique success callback should always run if request1 succeeds, even if request2 or request 3 fails.
}));

request2.pipe(tap(() => {
    //Unique success callback should always run if request2 succeeds, even if request1 or request 3 fails.
}));

request3.pipe(tap(() => {
    //Unique success callback should always run if request3 succeeds, even if request1 or request 2 fails.
}));

var observable = zip(request1, request2, request3);
observable.subscribe(() => {
    //Do something when all three execute successfully
});
3
how is this related to angular? - Jota.Toledo
@Jota.Toledo Did you downvote because I used Observable.create instead of httpClient.post in my example? - user1007817

3 Answers

2
votes

I believe this is expected and the appropriate behavior for what you're dealing with. You probably want to look at using piping the catchError lettable into each of your requests and returning an empty observable.

request1.pipe(tap(() => {
    //Unique success callback
}), catchError((err) => {
    return empty();
}));

That way you handle the error of that observable without breaking the new zip.

0
votes

Add catch block in your code where you can track the errors.

request1.pipe(tap(() => {
    //Unique success callback I want to run if request2 succeeds.
    //It should still run if request1 fails
}),
    catchError(// Error occured)
);
0
votes

@MichaelSolati is nearly correct, except

  • you should catch and return a value, e.g null if you wish to see output in subscribe() - because zip won't fire with empty()
  • your tap() callbacks are not in the zip() pipeline, they are separate branches which are not subscribed to, so are never activated.

Note that with changes to rxjs versions, the imports can get a bit tricky, for example zip is available as a function and as an operator.
With the rxjs.umd.js cdn used in the snippet below, I initially used the operator by mistake and it doesn't throw an error (but does not work).

I note from comments above you are doing this in an Angular context. If you still have a problem, please post your full Angular module with imports and we can resolve the difficulty.

console.clear()
//console.log(rxjs)

// Get the operators and creators
const tap = rxjs.operators.tap
const empty = rxjs.empty
const zip = rxjs.zip
const catchError = rxjs.operators.catchError
const of = rxjs.of
const throwError = rxjs.throwError


//var request1 = of(1)
var request1 = throwError('error') 
var request2 = of(2)
var request3 = of(3)

var req1 = request1.pipe(
  tap(() => console.log('request1')),
  catchError((err) => { 
    console.log('request1 has error')
    return of(null) 
  })
);

var req2 = request2.pipe(
  tap(() => { console.log('request2');})
);

var req3 = request3.pipe(
  tap(() => { console.log('request3');})
);

var myObservable = rxjs.zip(req1, req2, req3);
myObservable.subscribe(
  result => { console.log('result', result) }
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.2.0/rxjs.umd.js"></script>