75

我想要一个 for 循环,每次迭代都会调用异步函数。

在 for 循环之后,我想执行另一个代码块,但不是在 for 循环中的所有先前调用都已解决之前。

我目前的问题是,要么在所有异步调用完成之前执行for循环之后的代码块,要么根本不执行。

带有 FOR 循环的代码部分及其后的代码块(完整代码请参见fiddle):

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

我创建了一个 jsFiddle,可以在这里找到http://jsfiddle.net/riemersebastian/B43u6/3/

目前看起来执行顺序很好(参见控制台输出)。

我的猜测是,这仅仅是因为每个函数调用都会立即返回而不做任何实际工作。我试图用 setTimeout 延迟 defer.resolve 但失败了(即最后一个代码块从未执行过)。您可以在小提琴的注释块中看到它。

当我使用写入文件和从文件读取的实际函数时,最后一个代码块在最后一个写入操作完成之前执行,这不是我想要的。

当然,错误可能出在其中一个读/写函数中,但我想验证我在此处发布的代码没有任何问题。

4

4 回答 4

121

您需要使用的是$q.all,它将许多承诺组合成一个,只有在所有承诺都解决后才会解决。

在您的情况下,您可以执行以下操作:

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;
}
于 2014-01-10T23:28:33.097 回答
3

使用新的 ES7,您可以以更直接的方式获得相同的结果:

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

let results = await Promise.all(promises);

console.log(results);
于 2015-06-01T08:29:23.637 回答
1

您可以$q一起使用和“减少”,以链接承诺。

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;
}
于 2017-04-21T13:12:17.963 回答
0

这对我使用 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;
}
于 2017-11-04T01:04:47.390 回答