web-dev-qa-db-fra.com

Itérer sur tableau lorsque chaque boucle forEach est terminée

Je suis en train d'essayer de parcourir un tableau que je construis à partir de plusieurs appels http dans un angular.forEach()

la fonction

$scope.ticket_stats = function(){
    //cleaning variables
    $scope.data_set = [];
    $scope.closed_tickets = [];

    //fetching time stamps (Epoch)
    $scope.time_frame = time_period.days(7);

              //calling data using time stamps
              angular.forEach($scope.time_frame, function(item) {
                  //debug
                  console.log(item);

                  var promise = tickets.status("closed", item);

                  promise.success(function(data){
                      console.log(data);
                      $scope.closed_tickets.Push(data[0].datapoints[0][0]); // returns a numerical value
                  });

              });
              //SEE MESSAGE BELOW
              $scope.data_set.Push($scope.closed_tickets);

}

la dernière ligne $scope.data_set.Push() fonctionne mais s'incrémente dans le temps une fois que les appels renvoient des données. Je voudrais que cette ligne soit exécutée une fois que tout est dans la boucle for Each. J'ai besoin d'itérer sur le tableau $scope.closed_tickets pour pouvoir lire (additionner) des données qu'il contient et créer un second tableau.

voici les services utilisés dans cette fonction:

// CALL TICKETS STATS
app.service('tickets', function($http){

    this.status = function(status, date){
        var one_snap = date - 100;
        var url = "/url/render?format=json&target=sum(stats.tickets."+status+")&from="+one_snap+"&until="+date+"";
        return $http.get(url);
    };            
});

// TIME STAMPS MATHS
app.service('time_period', function(){
    var currentDate = parseInt((new Date).getTime()/1000);

    this.days = function(number){
        var pending = [];
        for (var i = number; i > 0; i--) {
            pending.Push(currentDate - (87677*i));
        }
        return pending;
    }; 
});

Je cherche des informations et découvre le service $q.all() mais je ne parviens pas à le faire fonctionner comme je le souhaite.

Tous les conseils seraient les bienvenus! Merci!

7
SKYnine

Vous pouvez utiliser $ q.all pour attendre la fin de plusieurs événements ansynchrones (promesses).

$scope.ticket_stats = function() {
    // list of all promises
    var promises = [];

    //cleaning variables
    $scope.data_set = [];
    $scope.closed_tickets = [];

    //fetching time stamps (Epoch)
    $scope.time_frame = time_period.days(7);

    //calling data using time stamps
    angular.forEach($scope.time_frame, function(item) {
        // create a $q deferred promise
        var deferred = $q.defer();
        //debug
        console.log(item);

        tickets.status("closed", item).success(function(data) {
            console.log(data);
            $scope.closed_tickets.Push(data[0].datapoints[0][0]);

            // promise successfully resolved
            deferred.resolve(data);
        });

        // add to the list of promises
        promises.Push(deferred.promise);
    });

    // execute all the promises and do something with the results
    $q.all(promises).then(
        // success
        // results: an array of data objects from each deferred.resolve(data) call
        function(results) {
            $scope.data_set.Push($scope.closed_tickets);
        },
        // error
        function(response) {
        }
    );
}

Tout d’abord, deferred représente un morceau de code dont l’exécution (asynchrone) prendra un temps inconnu. deferred.resolve(data) indique simplement que le code est terminé. Les données peuvent être n'importe quoi, un objet, une chaîne de caractères, mais ce sont généralement les résultats de votre code asynchrone. De même, vous pouvez rejeter une promesse avec deferred.reject(data) (peut-être qu'une erreur a été émise par le serveur). Encore une fois, les données peuvent être n'importe quoi mais ici, ce devrait probablement être la réponse d'erreur. 

deferred.promise renvoie simplement un objet de promesse. L'objet de promesse vous permet de définir des callbacks tels que .then(successFunction, errorFunction) pour que vous sachiez qu'une partie du code a été exécutée avant de passer à successFunction (ou errorFunction en cas d'échec). Dans notre cas, $q a la méthode .all qui attend qu'un tableau de promesses se termine, puis vous donne les résultats de toutes les promesses sous forme de tableau.

N'oubliez pas d'injecter le service $q.

21
csbarnes

Essayez de faire un tableau de promesses seulement, sans les résoudre pour le moment. Puis agrégez-les avec $ q.all (). Après la résolution des promesses agrégées, parcourez à nouveau le tableau de ces promesses. Maintenant, vous êtes sûr qu'ils sont tous résolus.

var promises = [];
angular.forEach($scope.time_frame, function(item) {
    promises.Push(tickets.status("closed", item));
});

var aggregatedPromise = $q.all(promises);

aggregatedPromise.success(function(){
    angular.forEach(promises, function(promise) {
        promise.success(function(data){
            $scope.closed_tickets.Push(data[0].datapoints[0][0]); // returns a numerical value
        });
    });
});

Peut-être que ce n’est pas le moyen le plus efficace de le faire, mais je pense que cela devrait résoudre votre problème.

1
miszczak

Même si vous mentionnez que $ q.all n'a pas fonctionné pour vous, car je ne vois pas pourquoi cela ne devrait pas, voici comment je procéderais.

En gros, vous voulez mapper un tableau d'éléments (horodatages) sur d'autres éléments (promesses dans ce cas car nous avons des appels asynchrones) et effectuer des actions sur le tableau résultant.

var promises = $scope.time_frame.map(function (item) {
    return tickets.status("closed", item);
});

Nous utilisons maintenant $q.all pour attendre que toutes les promesses soient résolues:

$q.all(promises).then(function (tickets) {
    $scope.data_set.Push(tickets);
});
1
getOffMyLawn