Home How should I set up asynchronous error handling with promises in angular?
Reply: 1

How should I set up asynchronous error handling with promises in angular?

Andrew
1#
Andrew Published in 2017-11-14 16:45:23Z

Currently, the flow in my application looks something like this

function searchAsync() {
    $scope.search.inProgress = true;

    return Service.search($scope.search.parameters).$promise
        .then(function (data) {
            $scope.search.data = data;
        }).catch(function (e) {
            displayError(e);
        }).finally(function () {
            $scope.search.inProgress = false;
        });
}

function initialize() {
    var promises = [];

    promises.push(setLookupsAsync($scope.categories, LookupResource.getCategories));
    promises.push(setLookupsAsync($scope.topics, LookupResource.getTopics));
    promises.push(setLookupsAsync($scope.races, LookupResource.getRaces));
    promises.push(setLookupsAsync($scope.genders, LookupResource.getGenders));

    $q.all(promises).then(function () {
        searchAsync()
    }).catch(function (e) {
        displayError(e);
    }).finally(function () {
        $scope.page.loaded = true;
    });
}

I've found that if the promise inside the searchAsync function fails, the error handling is local to that promise and the outside caller has no way of knowing if it failed or not. If I needed to access the $scope.search.data after the searchAsync call in the $q.all 'then' piece, I would have no way of knowing if it had no items due to a handled catch or due to no matching items. Is there a cleaner way I can set up my error handling when working with promises?

T.J. Crowder
2#
T.J. Crowder Reply to 2017-11-14 16:49:15Z

Is there a cleaner way I can set up my error handling when working with promises?

Yes: Don't handle it there.

Since searchAsync returns a promise, it shouldn't handle errors itself (other than optionally transforming them while keeping them as errors) unless it can meaningfully convert an error into a result (which it doesn't look like it can). Instead, it should let its caller handle errors. The catch block you currently have transforms an error (a rejected promise) into a success (a resolved promise) with the value undefined. (It's not just you, this is a common mistake.)

Either remove the catch:

function searchAsync() {
    $scope.search.inProgress = true;

    return Service.search($scope.search.parameters).$promise
        .then(function (data) {
            $scope.search.data = data;
        }).finally(function () {
            $scope.search.inProgress = false;
        });
}

...or keep it to call displayError but rethrow the error:

function searchAsync() {
    $scope.search.inProgress = true;

    return Service.search($scope.search.parameters).$promise
        .then(function (data) {
            $scope.search.data = data;
        }).catch(function (e) {
            displayError(e);
            throw e; // <=========
        }).finally(function () {
            $scope.search.inProgress = false;
        });
}

In general, a function should either handle errors or return the promise chain, but rarely both. Letting errors propagate through the chain to the top level leaves error handling to the top level, while optionally letting all levels know they occurred. (Again: You can transform an error, or "tap" it by doing something and then re-throwing; those are fine. Silently converting failure to success-with-undefined is not. :-) )

You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.356182 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO