Angularjs $ q.all


105

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

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の設定に問題があると思います。


1
あなたはどのような行動を見ていますか?それはあなたを呼びますthen(datas)か?pushこれだけを試してください:promises.push(deffered);
Davin Tryon

@ themyth92私の解決策を試しましたか?
Ilan Frumer 2014年

私は試しましたが、どちらの方法も私のケースで機能します。しかし、正解は@Llan Frumerにします。本当にありがとうございました。
themyth92 2014年

1
なぜ既存の約束を約束するのですか?$ httpはすでにpromiseを返します。$ q.deferの使用は不必要です。
ピートアルヴィン

1
それは:) ではありdeferredませんdeffered
クリストフ・ルシー

回答:


225

JavaScriptではありませblock-level scopesfunction-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);
})
  • あなたが書くときvar deferred= $q.defer();、それはだforループ内で掲揚関数の先頭に、それはJavascriptをの関数スコープの外側にこの変数を宣言することを意味しますfor loop
  • 各ループでは、最後の据え置きが前のループをオーバーライドしており、そのオブジェクトへの参照を保存するためのブロックレベルのスコープはありません。
  • 非同期コールバック(成功/エラー)が呼び出されると、それらは最後の遅延オブジェクトのみを参照し、それのみが解決されます。そのため、$ q.allは解決されません
  • 必要なのは、反復するアイテムごとに無名関数を作成することです。
  • 関数にはスコープがあるため、遅延オブジェクトへの参照は、closure scope関数が実行された後でも保持されます。
  • #dfsqがコメントしたように:$ http自体がpromiseを返すため、新しい遅延オブジェクトを手動で構築する必要はありません。

ソリューション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);
}

14
いい答えだ。1つの追加:$ http自体がpromiseを返すため、新しい遅延オブジェクトを構築する必要はありません。したがって、短くすることができます:plnkr.co/edit/V3gh7Roez8WWl4NKKrqM
p=

「var deferred = $ q.defer();と記述すると、forループ内で関数の先頭に移動します。」この部分がわからないのですが、その理由を教えてください。
themyth92 2014年

私は知っています、私は実際に同じことをします。
dfsq 2014年

4
map一連の約束を構築するためのの使用が大好きです。とてもシンプルで簡潔です。
Drumbeg

1
宣言は巻き上げられますが、割り当てはそのままです。また、「let」ステートメントを使用したブロックレベルのスコープもあります。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…を
Spencer

36

$ httpも約束です。もっと簡単にすることができます:

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

2
はい、受け入れられた答えはアンチパターンの集まりにすぎません。
Roamer-1888 14年

.then()OPはコントローラーでそれをすべて実行したいので、この節は省略できますが、原則は完全に正しいです。
Roamer-1888 14年

2
もちろん、then句は省略できます。すべてのHTTPリクエストをログに記録する場合に備えて追加しました。すべてのHTTPリクエストを常に追加し、グローバルなonErrorコールバックを使用して、すべてのサーバー例外を処理します。
ゼルコティン2014

1
あなたができる@Zerkotin throwから.then後でハンドル、それを両方のために、に公開$exceptionHandlerあなたのその悩みや地球を救うべきです。
Benjamin Gruenbaum 2014

いいね。これは基本的に、受け入れられた回答の最後のソリューション/例と同じアプローチです。
Niko Bellic

12

問題は、deffered.promiseいつ追加するdefferedかということです。それ自体が追加すべき約束です。

ラップさ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);
        }

これはdeferredオブジェクトの配列を返すだけなので、チェックしました。
Ilan Frumer 2014年

:私は彼の言う、唯一のコンソールが言う、あなたはそれが働いていない見ることができるかわからないplnkr.co/edit/J1ErNncNsclf3aU86D7Z?p=preview
宜蘭Frumer

4
また、ドキュメントには、$q.all遅延オブジェクトではなくpromise を取得することが明記されています。OPの本当の問題はスコーピングにあり、最後の延期のみが解決されているためです
Ilan Frumer 2014年

Ilan、deferオブジェクトとのもつれをほぐしてくれてありがとうpromises。あなたall()も私の問題を修正しました。
ロスロジャース

この問題は2つの回答で解決されました。問題は、スコーピングまたは可変ホイストであり、何と呼んでもかまいません。
ゼルコティン2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.