web-dev-qa-db-ja.com

angular $ q、forループ内およびforループ後に複数のプロミスをチェーンする方法

繰り返しごとに非同期関数を呼び出すforループが必要です。

Forループの後、別のコードブロックを実行したいが、forループ内の以前のすべての呼び出しが解決される前ではない。

現時点での問題は、すべての非同期呼び出しが完了する前にforループ後のコードブロックが実行されるORことです。

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を遅延させようとしましたが、失敗しました(つまり、最後のコードブロックは実行されませんでした)。フィドルのコメントブロックで確認できます。

ファイルへの書き込みとファイルからの読み取りを行う実際の関数を使用すると、最後の書き込み操作が完了する前に最後のコードブロックが実行されます。

もちろん、これらの読み取り/書き込み関数の1つにエラーがある可能性がありますが、ここに投稿したコードに問題がないことを確認したいと思います。

75
SebastianRiemer

使用する必要があるのは $ q.all で、これはいくつかのプロミスを1つに結合し、すべてのプロミスが解決されたときにのみ解決されます。

あなたの場合、次のようなことができます:

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

新しいES7を使用すると、はるかに簡単な方法で同じ結果を得ることができます。

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

let results = await Promise.all(promises);

console.log(results);
3

これは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

$qと 'reduce'を一緒に使用して、約束を連鎖させることができます。

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