问题
I would like to combine/merge multiple Observables and when each of them is completed execute a finally function. The merge
operator seems to execute each subscription in parallel, which is what I need, but if any of them throws an error the execution is halted.
RxJS version 4 has an operator mergeDelayError that should keep the all subscriptions executing till all of them are completed, but this operator isn't implemented in version 5.
Should I revert to a different operator?
var source1 = Rx.Observable.of(1,2,3).delay(3000);
var source2 = Rx.Observable.throw(new Error('woops'));
var source3 = Rx.Observable.of(4,5,6).delay(1000);
// Combine the 3 sources into 1
var source = Rx.Observable
.merge(source1, source2, source3)
.finally(() => {
// finally is executed before all
// subscriptions are completed.
console.log('finally');
});
var subscription = source.subscribe(
x => console.log('next:', x),
e => console.log('error:', e),
() => console.log('completed'));
JSBin
回答1:
I think you can simulate the same behavior by using catch()
. You'll just need to append it to every source Observable:
const sources = [source1, source2, source3].map(obs =>
obs.catch(() => Observable.empty())
);
Rx.Observable
.merge(sources)
.finally(...)
...
回答2:
If you don't want to swallow your errors, but want to delay them to the end, you can:
const mergeDelayErrors = [];
const sources = [source1, source2, source3].map(obs => obs.catch((error) => {
mergeDelayErrors.push(error);
return Rx.Observable.empty();
}));
return Rx.Observable
.merge(...sources)
.toArray()
.flatMap(allEmissions => {
let spreadObs = Rx.Observable.of(...allEmissions);
if (mergeDelayErrors.length) {
spreadObs = spreadObs.concat(Rx.Observable.throw(mergeDelayErrors));
}
return spreadObs;
})
You may want to throw only the first error, or create a CompositeError. I'm not sure how mergeDelayErrors originally behaved when multiple errors were thrown.
Unfortunately, because this implementation must wait until the all observables complete before emitting errors, it also waits until all observables complete before emitting next. This is likely not the original behavior of mergeDelayError, which is supposed to emit as a stream, rather than emitting them all at the end.
回答3:
We can avoid blocking the stream by collecting the errors and emitting them at the end.
function mergeDelayError(...sources) {
const errors = [];
const catching = sources.map(obs => obs.catch(e => {
errors.push(e);
return Rx.Observable.empty();
}));
return Rx.Observable
.merge(...catching)
.concat(Rx.Observable.defer(
() => errors.length === 0 ? Rx.Observable.empty() : Rx.Observable.throw(errors)));
}
const source1 = Rx.Observable.of(1,2,3);
const source2 = Rx.Observable.throw(new Error('woops'));
const source3 = Rx.Observable.of(4,5,6);
mergeDelayError(source1, source2, source3).subscribe(
x => console.log('next:', x),
e => console.log('error:', e),
() => console.log('completed'));
来源:https://stackoverflow.com/questions/45738571/rxjs5-merge-and-error-handling