简体   繁体   中英

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

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?

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. :-) )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM