Angularjs $ q.all

105

Ich habe $ q.all in anglejs implementiert, aber ich kann den Code nicht zum Laufen bringen. Hier ist mein Code:

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

Und hier ist mein Controller, der die Dienste aufruft:

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

})

Ich denke, es gibt ein Problem beim Einrichten von $ q.all in meinem Dienst.

themyth92
quelle
1
Welches Verhalten sehen Sie? Ruft es in Ihre then(datas)? Versuchen Sie genau pushdies:promises.push(deffered);
Davin Tryon
@ themyth92 hast du meine lösung ausprobiert?
Ilan Frumer
Ich habe es versucht und beide Methoden funktionieren in meinem Fall. Aber ich werde @Llan Frumer als die richtige Antwort machen. Wirklich danke euch beiden.
themyth92
1
Warum versprechen Sie ein bestehendes Versprechen? $ http gibt bereits ein Versprechen zurück. Die Verwendung von $ q.defer ist überflüssig.
Pete Alvin
1
Es ist deferrednicht deffered:)
Christophe Roussy

Antworten:

225

In Javascript gibt es nicht block-level scopesnur function-level scopes:

Lesen Sie diesen Artikel über JavaScript Scoping und Hoisting .

Sehen Sie, wie ich Ihren Code debuggt habe:

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);
})
  • Wenn Sie schreiben var deferred= $q.defer();innerhalb einer for - Schleife es gehisst an die Spitze der Funktion, bedeutet es , dass Javascript auf dem Funktionsumfang außerhalb der diese Variable deklariert for loop.
  • Mit jeder Schleife der letzten latenten die vorherigen ist überschreibt, ist es kein Blockebene Umfang einen Verweis auf das Objekt zu speichern.
  • Wenn asynchrone Rückrufe (Erfolg / Fehler) aufgerufen werden, verweisen sie nur auf das letzte zurückgestellte Objekt und nur es wird aufgelöst, sodass $ q.all niemals aufgelöst wird, da es immer noch auf andere zurückgestellte Objekte wartet.
  • Sie müssen für jedes Element, das Sie iterieren, eine anonyme Funktion erstellen.
  • Da Funktionen Bereiche haben, bleibt der Verweis auf die zurückgestellten Objekte closure scopeauch nach Ausführung von Funktionen erhalten.
  • Wie #dfsq kommentierte: Es ist nicht erforderlich, ein neues zurückgestelltes Objekt manuell zu erstellen, da $ http selbst ein Versprechen zurückgibt.

Lösung mit angular.forEach:

Hier ist ein Demo-Plunker: 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);
}

Meine Lieblingsmethode ist Array#map:

Hier ist ein Demo-Plunker: 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);
}
Ilan Frumer
quelle
14
Gute Antwort. Eine Ergänzung: Es ist nicht erforderlich, eine neue Verschiebung zu erstellen, da $ http selbst ein Versprechen zurückgibt. Es kann also kürzer sein: plnkr.co/edit/V3gh7Roez8WWl4NKKrqM?p=preview
dfsq
"Wenn Sie var deferred = $ q.defer () schreiben, wird es innerhalb einer for-Schleife an den Anfang der Funktion gehievt." Ich verstehe diesen Teil nicht. Können Sie den Grund dafür erklären?
themyth92
Ich weiß, ich würde das auch tun.
dfsq
4
Ich liebe mapes, eine Reihe von Versprechungen zu machen. Sehr einfach und prägnant.
Drumbeg
1
Es ist zu beachten, dass die Deklaration gehisst wird, die Zuordnung jedoch dort bleibt, wo sie ist. Außerdem gibt es jetzt ein Scoping auf Blockebene mit der Anweisung 'let'. Siehe developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Spencer
36

$ http ist auch ein Versprechen, Sie können es einfacher machen:

return $q.all(tasks.map(function(d){
        return $http.post('upload/tasks',d).then(someProcessCallback, onErrorCallback);
    }));
Zerkotin
quelle
2
Ja, die akzeptierte Antwort ist nur eine Ansammlung von Anti-Mustern.
Roamer-1888
Ich denke, Sie können die .then()Klausel weglassen, da das OP all das in seinem Controller tun möchte, aber das Prinzip ist völlig korrekt.
Roamer-1888
2
Natürlich können Sie die then-Klausel weglassen. Ich habe sie hinzugefügt, falls Sie alle HTTP-Anforderungen protokollieren möchten. Ich füge sie immer hinzu und verwende einen globalen onError-Rückruf, um alle Serverausnahmen zu behandeln.
Zerkotin
1
@Zerkotin können Sie throwvon einem .then, um es später zu behandeln und es auszusetzen $exceptionHandler, was Ihnen diesen Ärger und eine globale ersparen sollte.
Benjamin Gruenbaum
Nett. Dies ist im Wesentlichen der gleiche Ansatz wie die letzte Lösung / das letzte Beispiel der akzeptierten Antwort.
Niko Bellic
12

Das Problem scheint zu sein, dass Sie das deffered.promiseWann defferedselbst hinzufügen, das Versprechen, das Sie hinzufügen sollten:

Versuchen Sie, zu zu wechseln, promises.push(deffered);damit Sie das unverpackte Versprechen nicht zum Array hinzufügen.

 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);
        }
Davin Tryon
quelle
Dies gibt nur ein Array von verzögerten Objekten zurück, ich habe es überprüft.
Ilan Frumer
Ich weiß nicht, was er sagt, nur was die Konsole sagt. Sie können sehen, dass es nicht funktioniert: plnkr.co/edit/J1ErNncNsclf3aU86D7Z?p=preview
Ilan Frumer
4
Auch die Dokumentation sagt deutlich, dass $q.allVersprechen keine zurückgestellten Objekte bekommen. Das eigentliche Problem des OP ist das Scoping und weil nur der letzte Aufschub gelöst wird
Ilan Frumer
Ilan, danke für das Entwirren von deferObjekten und promises. Sie haben auch mein all()Problem behoben .
Ross Rogers
Das Problem wurde in 2 Antworten behoben. Das Problem ist das Scoping oder das variable Heben, wie auch immer Sie es nennen möchten.
Zerkotin