API call inside for loop in angular

2019-09-04 12:05发布

问题:

I'm trying to create an Ionic app and for this I have to make some HTTP GET request inside a for loop but it appears that angular does not wait for the data before showing them.

Here is a code I'm using.

$http.get(ApiUrl.get() + '/Threads' + '?access_token=' + $scope.userToken + filterString + "&filter[order]=created%20desc")
.success(function(data, status, headers, config) {

    var i=0;

    for(thread in data)
    {
        $scope.threadObj = data[i];
        var threadId = $scope.threadObj.id;
        $scope.threadPostNumber;

        //On récupére chaque nombre de post
        $http.get(ApiUrl.get() + '/Threads/' + threadId + '/posts/count' + '?access_token=' + $scope.userToken)
            .success(function(data, status, headers, config) {
                $scope.threadPostNumber = data.count;
            })
            .error(function(data, status, headers, config) {
                alert("Connection error, please try again.");
                $location.path("/app/carte");
            });

        $scope.threadObj.count = $scope.threadPostNumber;
        $scope.threads[i] = $scope.threadObj;

        i++;
    }
})
.error(function(data, status, headers, config) {
    alert("Connection error, please try again.");
    $location.path("/app/carte");
});

The first HTTP get is done and the data can be show within the foreach but when I try to add additionnal data to the original ones with a second get request nothing is created or sometimes only the last value is shown for every one.

回答1:

The problem stems from the API call being asynchronous, and much slower than the for loop.

The $http.get posts the request, but it doesn't get the response until long after the for loop completes. As a consequence $scope.threadPostNumber inside the promise success callback is going to be set after it is assigned in:

$scope.threadObj.count = $scope.threadPostNumber

So effectively this assignment is useless.

In order to fix, use tail recursion or Promise objects in order to make the calls consecutive.

You can also properly scope the current thread object so that when the promise succeeds you will be modifying the correct thread:

$http.get(ApiUrl.get() + '/Threads' + '?access_token=' + $scope.userToken + filterString + "&filter[order]=created%20desc")
.success(function(data, status, headers, config) {

    data.forEach(function(thread, i) {

        $scope.threads[i] = thread;

        var threadId = thread.id;

        $http.get(ApiUrl.get() + '/Threads/' + threadId + '/posts/count?access_token=' + $scope.userToken)
            .success(function(data, status, headers, config) {

                $scope.threads[i].count = data.count;
            })
            .error(function(data, status, headers, config) {

                alert('Connetion error, please try again.');
                $location.path('/app/carte');
            });
    });
})
.error(function(data, status, headers, config) {
    alert("Connection error, please try again.");
    $location.path("/app/carte");
});

Because this uses forEach (see the MDN page), thread and i are scoped to the function, so for the lifetime of that function and any functions that it calls or creates (like the promise success callback), thread and i will remain the value passed into the function. This ensures that no matter what order the HTTP requests return in, you'll be setting count on the proper thread.