web-dev-qa-db-fra.com

angular $ q, Comment chaîner plusieurs promesses dans et après une boucle for

Je veux avoir une boucle for qui appelle des fonctions asynchrones à chaque itération.

Après la boucle for, je souhaite exécuter un autre bloc de code, mais pas avant que tous les appels précédents de la boucle for aient été résolus.

Mon problème pour le moment est que soit le bloc de code après la boucle for est exécuté avant que tous les appels asynchrones ne soient terminés OR il n'est pas exécuté du tout.

La partie de code avec la boucle FOR et le bloc de code après (pour le code complet, voir violon ):

[..]
function outerFunction($q, $scope) {
    var defer = $q.defer();    
    readSome($q,$scope).then(function() {
        var promise = writeSome($q, $scope.testArray[0])
        for (var i=1; i < $scope.testArray.length; i++) {
             promise = promise.then(
                 angular.bind(null, writeSome, $q, $scope.testArray[i])
             );                                  
        } 
        // this must not be called before all calls in for-loop have finished
        promise = promise.then(function() {
            return writeSome($q, "finish").then(function() {
                console.log("resolve");
                // resolving here after everything has been done, yey!
                defer.resolve();
            });   
        });        
    });   

    return defer.promise;
}

J'ai créé un jsFiddle que vous pouvez trouver ici http://jsfiddle.net/riemersebastian/B43u6/3/ .

Pour le moment, il semble que l'ordre d'exécution soit correct (voir la sortie de la console).

J'imagine que c'est simplement parce que chaque appel de fonction revient immédiatement sans effectuer de travail réel. J'ai essayé de retarder la résolution de différé avec setTimeout mais j'ai échoué (c'est-à-dire que le dernier bloc de code n'a jamais été exécuté). Vous pouvez le voir dans le bloc dépassé du violon.

Lorsque j'utilise les fonctions réelles qui écrivent dans un fichier et lisent dans un fichier, le dernier bloc de code est exécuté avant la fin de la dernière opération d'écriture, ce qui n'est pas ce que je veux.

Bien sûr, l’erreur pourrait être dans l’une de ces fonctions de lecture/écriture, mais je voudrais vérifier que le code que j’ai posté ici n’est pas faux.

75
SebastianRiemer

Ce que vous devez utiliser est $ q.all qui combine plusieurs promesses en une seule qui n’est résolue que lorsque toutes les promesses sont résolues.

Dans votre cas, vous pourriez faire quelque chose comme:

function outerFunction() {

    var defer = $q.defer();
    var promises = [];

    function lastTask(){
        writeSome('finish').then( function(){
            defer.resolve();
        });
    }

    angular.forEach( $scope.testArray, function(value){
        promises.Push(writeSome(value));
    });

    $q.all(promises).then(lastTask);

    return defer.promise;
}
120
Gruff Bunny

Avec la nouvelle ES7, vous pouvez obtenir le même résultat de manière beaucoup plus simple:

let promises =  angular.forEach( $scope.testArray, function(value){
    writeSome(value);
});

let results = await Promise.all(promises);

console.log(results);
3

Cela a fonctionné pour moi en utilisant la syntaxe ES5

function outerFunction(bookings) {

    var allDeferred = $q.defer();
    var promises = [];

    lodash.map(bookings, function(booking) {
        var deferred = $q.defer();

        var query = {
            _id: booking.product[0].id,
            populate: true
        }

        Stamplay.Object("product").get(query)
        .then(function(res) {
            booking.product[0] = res.data[0];
            deferred.resolve(booking)
        })
        .catch(function(err) {
            console.error(err);
            deferred.reject(err);
        });

        promises.Push(deferred.promise);
    });

    $q.all(promises)
    .then(function(results) { allDeferred.resolve(results) })
    .catch(function(err) { allDeferred.reject(results) });

    return allDeferred.promise;
}
0
Ben Cochrane

Vous pouvez utiliser $q et 'réduire' ensemble, pour enchaîner les promesses.

function setAutoJoin() {
    var deferred = $q.defer(), data;
    var array = _.map(data, function(g){
            return g.id;
        });

    function waitTillAllCalls(arr) {
        return arr.reduce(function(deferred, email) {
            return somePromisingFnWhichReturnsDeferredPromise(email);
        }, deferred.resolve('done'));
    }

    waitTillAllCalls(array);

    return deferred.promise;
}
0
STEEL