Pregunta angular $ q, cómo encadenar múltiples promesas dentro y después de un for-loop


Quiero tener un for-loop que llame a las funciones asincrónicas en cada iteración.

Después de for-loop quiero ejecutar otro bloque de código, pero no antes de que se hayan resuelto todas las llamadas anteriores en for-loop.

Mi problema en este momento es que, o bien el bloque de código después de for-loop se ejecuta antes de que todas las llamadas asincrónicas hayan finalizado O no se ejecute en absoluto.

El código parte con el bucle FOR y el bloque de código después de él (para ver el código completo, consulte violín)

[..]
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;
}

Creé un jsFiddle que se puede encontrar aquí http://jsfiddle.net/riemersebastian/B43u6/3/.

Por el momento, parece que el orden de ejecución es bueno (consulte la salida de la consola).

Mi suposición es que esto es simplemente porque cada llamada de función regresa inmediatamente sin hacer ningún trabajo real. He intentado retrasar la resolución defer.resolve con setTimeout pero falló (es decir, el último bloque de código nunca se ejecutó). Puedes verlo en el bloque outcommented en el violín.

Cuando uso las funciones reales que escriben en un archivo y leen desde el archivo, el último bloque de código se ejecuta antes de que finalice la última operación de escritura, que no es lo que quiero.

Por supuesto, el error podría estar en una de esas funciones de lectura / escritura, pero me gustaría verificar que no haya nada de malo en el código que he publicado aquí.


75
2018-01-09 15:31


origen


Respuestas:


Lo que necesitas usar es $ q.all que combina una serie de promesas en una que solo se resuelve cuando se resuelven todas las promesas.

En tu caso, podrías hacer algo como:

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;
}

118
2018-01-10 23:28



Con el nuevo ES7 puedes obtener el mismo resultado de una manera mucho más directa:

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

let results = await Promise.all(promises);

console.log(results);

2
2018-06-01 08:29



Puedes usar $q y 'reducir' juntos, para encadenar las promesas.

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
2018-04-21 13:12



Esto funcionó para mí usando la sintaxis de 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
2017-11-04 01:04