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.


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

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

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

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.