web-dev-qa-db-ja.com

Angularjs $ q.all

$ q.allをangularjsに実装しましたが、コードを機能させることができません。これが私のコードです:

UploadService.uploadQuestion = function(questions){

        var promises = [];

        for(var i = 0 ; i < questions.length ; i++){

            var deffered  = $q.defer();
            var question  = questions[i]; 

            $http({

                url   : 'upload/question',
                method: 'POST',
                data  : question
            }).
            success(function(data){
                deffered.resolve(data);
            }).
            error(function(error){
                deffered.reject();
            });

            promises.Push(deffered.promise);
        }

        return $q.all(promises);
    }

そして、これが私のサービスを呼び出すコントローラーです。

uploadService.uploadQuestion(questions).then(function(datas){

   //the datas can not be retrieved although the server has responded    
}, 
function(errors){ 
   //errors can not be retrieved also

})

私のサービスでは$ q.allの設定に問題があると思います。

104
themyth92

Javascriptでは、block-level scopesのみfunction-level scopesはありません。

javaScript Scoping and Hoisting についてのこの記事を読んでください。

私がどのようにあなたのコードをデバッグしたかを見てください:

var deferred = $q.defer();
deferred.count = i;

console.log(deferred.count); // 0,1,2,3,4,5 --< all deferred objects

// some code

.success(function(data){
   console.log(deferred.count); // 5,5,5,5,5,5 --< only the last deferred object
   deferred.resolve(data);
})
  • Forループの中にvar deferred= $q.defer();を書くと、それは関数の先頭にhoistedされ、それはjavascriptがfor loopの外側の関数スコープでこの変数を宣言することを意味します。
  • 各ループでは、最後の延期が前のループをオーバーライドしているため、そのオブジェクトへの参照を保存するためのブロックレベルのスコープはありません。
  • 非同期コールバック(成功/エラー)が呼び出されると、それらは最後のdeferred objectのみを参照し、それだけが解決されるので、$ q.allは解決されることはありません他の据え置きオブジェクト用。
  • あなたが必要とするのはあなたが繰り返す各項目のために無名関数を作成することです。
  • 関数にはスコープがあるため、遅延オブジェクトへの参照は、関数が実行された後もclosure scopeに保存されます。
  • #dfsqがコメントしたように:$ http自身が約束を返すので、新しい遅延オブジェクトを手動で構築する必要はありません。

angular.forEachによる解決策:

これがデモプランカーです: http://plnkr.co/edit/NGMp4ycmaCqVOmgohN53?p=preview

UploadService.uploadQuestion = function(questions){

    var promises = [];

    angular.forEach(questions , function(question) {

        var promise = $http({
            url   : 'upload/question',
            method: 'POST',
            data  : question
        });

        promises.Push(promise);

    });

    return $q.all(promises);
}

私のお気に入りの方法はArray#mapを使うことです。

これはデモプランカーです: http://plnkr.co/edit/KYeTWUyxJR4mlU77svw9?p=preview

UploadService.uploadQuestion = function(questions){

    var promises = questions.map(function(question) {

        return $http({
            url   : 'upload/question',
            method: 'POST',
            data  : question
        });

    });

    return $q.all(promises);
}
222
Ilan Frumer

$ httpも約束です、あなたはそれをより簡単にすることができます:

return $q.all(tasks.map(function(d){
        return $http.post('upload/tasks',d).then(someProcessCallback, onErrorCallback);
    }));
35
Zerkotin

defferedがそれ自身が追加すべき約束であるとき、問題はあなたがdeffered.promiseを追加しているということのようです:

promises.Push(deffered);に変更して、ラップされていないプロミスを配列に追加しないようにしてください。

 UploadService.uploadQuestion = function(questions){

            var promises = [];

            for(var i = 0 ; i < questions.length ; i++){

                var deffered  = $q.defer();
                var question  = questions[i]; 

                $http({

                    url   : 'upload/question',
                    method: 'POST',
                    data  : question
                }).
                success(function(data){
                    deffered.resolve(data);
                }).
                error(function(error){
                    deffered.reject();
                });

                promises.Push(deffered);
            }

            return $q.all(promises);
        }
12
Davin Tryon