I'm working on an Angular application which shows a list of items fetched from a RESTful API. The content of the list depends on a query. The query can be passed either by filling in an input field, using a submit button or by adding it to the URL as query parameter.
To make sure everything runs in order and prevent asynchronous issues, I’m using RxJS.
Now I’m wondering how I can handle errors, since they can occur in the middle of a stream, eg. when the HTTP-request fails.
These are two Observables
, which both send a sequence of queries.
// first observable for the submit button
submitFormObservable = $scope.$createObservableFunction('search');
// second observable for when the input value changes
inputObservable = $scope.$toObservable('query')
.map(function (change) {
return change.newValue;
});
The Observable
below triggers when the query has been changed and fetches the results from the API.
var mainStream = Rx.Observable.merge([submitFormObservable, inputObservable])
.where(function (query) {
return query && query.length > 0;
})
.debounce(400)
.distinctUntilChanged()
.select(getResultsForQuery)
.switchLatest();
Now I don’t know how to handle errors when eg. the getResultsForQuery
throws an error.
I would like to display the error instead of the results, but prevent the Observable
from handling new events.
Currently I've solved it by creating two new streams from the Observable, one to handle the result when successful and one when an error occurs. The response data for when the query was invalid contains an error
property.
Success stream
// stream containing the album information from LastFm
mainStream
.filter(function (response) {
return !response.data.error;
})
.map(function (response) {
return response.data.result;
})
.subscribe(function (result) {
$scope.error = undefined;
$scope.result = result;
});
Error stream
mainStream
.filter(function (response) {
return response.data.error;
})
.map(function (response) {
return response.data;
});
.subscribe(function (error) {
$scope.result = [];
$scope.error = error;
});
I've read about throwing and catching errors, but the problem here is the stream seems to stop after the first error and doesn't trigger new events.
Using onErrorResumeNext
is described in the docs to ignore errors and make sure the stream continues after an error. But I can't find a way to correctly 'handle' the error and show it to the end user.
Is there a recommended approach on how to handle errors in a situation like this? Is it necessary to create two streams for this, or is it recommended to throw an exception?
It's important that the user knows something went wrong, and the stream doesn't stop after the first error.
The issue with catching logic is that it effectively terminates the sequence before it, which is why if you use it in your top level stream it will stop after a single exception. I would recommend that you wrap your catch logic inside of a flatMapLatest
. Then you can catch on the inner stream and transform the data to conform with what your downstream observers
expect.
Something like this:
var mainStream = Rx.Observable.merge([submitFormObservable, inputObservable])
.where(function (query) {
return query && query.length > 0;
})
.debounce(400)
.distinctUntilChanged()
.selectSwitch(function(input) {
return getResultsForQuery(input)
.map(function(response) {
return {result : response.data.result};
})
.catch(function(e) {
return Rx.Observable.just({error : error});
});
});
mainStream.subscribe(function(r) {
$scope.result = r.result || [];
$scope.error = r.error;
});