2016-01-19 9 views
5

Ich versuche, $ Q.all zu verwenden, um zu warten, bis alle Versprechen gelöst sind, aber es heißt, nachdem das erste Versprechen abgeschlossen ist!

Was mache ich falsch?

function sendAudits(audits) { 
    var promises = []; 

    $scope.sendAudits = { 
     progress: 0 
    }; 
    angular.forEach(audits, function (audit, idAudit) { 
     promises.push(saveAudit(audit)); 
    }); 

    $q 
     .all(promises) 
     .then(function (data) { 
      console.log(data); 
     }, function (errors) { 
      console.log(errors); 
     }); 
} 

function saveAudit(audit) { 
    var filename = audit.header.id + ".txt"; 

    return $http({ 
     method: 'PUT', 
     url: '/audits/audits.php?filename=' + encodeURIComponent(filename), 
     data: AuditSvc.getPlainAudit(audit.header.id) 
    }).finally(function() { 
     $scope.sendAudits.progress += 1; 
     console.log("FINALLY: " + audit.header.id); 
    }); 
} 

EDIT

ein wenig tiefer, das Problem analysieren, tritt diese Situation, wenn einige der Antworten Fehler sind. Zum Beispiel, wenn der Server header("HTTP/1.0 418 I'm A Teapot: " . $filename); zurückgibt, Client-Konsole wäre wie:

PUT http://localhost:8182/audits/audits.php?filename=1.txt 418 (I'm A Teapot: 1.txt) 
FINALLY: 1 
Object {data: "", status: 418, config: Object, statusText: "I'm A Teapot: 1.txt"} 
PUT http://localhost:8182/audits/audits.php?filename=2.txt 418 (I'm A Teapot: 2.txt) 
FINALLY: 2 
PUT http://localhost:8182/audits/audits.php?filename=3.txt 418 (I'm A Teapot: 3.txt) 
FINALLY: 3 
PUT http://localhost:8182/audits/audits.php?filename=4.txt 418 (I'm A Teapot: 4.txt) 
FINALLY: 4 
+1

Was sehen Sie in der Konsole? – yeouuu

+0

Die Antwort für den ersten Aufruf von saveAudit: erster $ http Aufruf (Audits hat 4 Elemente). Das Seltsame ist, dass der Fortschritt in "finally" 4 Mal erhöht wird (einer für jedes Audit). – Miquel

Antwort

2

Die Winkel Dokumentation nicht ins Detail geht, aber ich glaube, $q.all() verhält sich in diesem Fall die gleiche Weise wie die es2015 Promise.all():

Wenn eine der abgegebenen Versprechen ablehnt, verwirft die gesamte Verheißung sofort mit dem Wert des Versprechens, das abgelehnt wurde, und verwirft alle anderen Versprechen, unabhängig davon, ob sie gelöst haben oder nicht.

Sehr wahrscheinlich passiert hier, dass mindestens eine Ihrer Anfragen fehlschlägt. Ihre Protokollanweisungen unterscheiden nicht, ob $q.all() erfolgreich war oder fehlgeschlagen ist, aber wenn alles fehlschlägt, sehen Sie nur den ersten Fehler.

Die Quelle des Angebots finden Sie unter https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all.

Edit:

Wenn Sie alle Antworten zu bekommen, auch wenn einige nicht, dann sollten Sie einen catch Handler in saveAudit fügen Sie die Ausfälle in erfolgreiche Antworten zu konvertieren:

function saveAudit(audit) { 
    var filename = audit.header.id + ".txt"; 

    return $http({ 
     method: 'PUT', 
     url: '/audits/audits.php?filename=' + encodeURIComponent(filename), 
     data: AuditSvc.getPlainAudit(audit.header.id) 
    }).catch(function(error) { 
     return { error:error}; 
    }) 
    .finally(function() { 
     $scope.sendAudits.progress += 1; 
     console.log("FINALLY: " + audit.header.id); 
    }); 
} 

und dann müssen Sie jede Antwort überprüfen, um zu sehen, ob sie einen Fehler oder gültige Daten enthält.

+0

Danke, das habe ich gerade bemerkt (siehe Bearbeiten in der Frage). Kannst du dieses Verhalten umgehen? Ich habe auch gesehen, dass, wenn alle Versprechen abgelehnt werden, der Fang-Callback nur einmal aufgerufen wird, nicht mit allen Fehlern. Zumindest würde man erwarten, dass der Fehler-Callback mit allen Fehlern aufgerufen wird, nicht nur mit dem ersten ... – Miquel

+0

Wenn Sie alle Fehler bekommen wollen, müssen Sie sie in Erfolge umwandeln. Siehe Bearbeiten. – Duncan

2

Wie von anderen erwähnt, ist $q.allnicht belastbar. Wenn eines der Versprechen abgelehnt wird, wird $q.all mit dem ersten Fehler abgelehnt.

Um ein elastischen Verbund Versprechen zu schaffen, das ist ein Versprechen, das für alle Versprechungen wartet bestanden oder nicht bestanden abzuschließen, verwenden Sie .catch auf jedem einzelnen Versprechen konvertieren die verworfenen Versprechen auf eine erfolgreiche Versprechen.

var resilientPromises = []; 

angular.forEach(promises, function(p) { 
    var resilientP = p.catch(function(result) { 
     //return to convert rejection to success 
     return result; 
    }); 
    resilientPromises.push(resilientP); 
}); 

$q.all(resilientPromises).then(function (results) { 
    //process results 
}); 

Die beiden Dinge nehmen von dieser Antwort entfernt:

  1. Ein $q.all Versprechen ist nicht elastisch. Es wird mit dem ersten abgelehnten Versprechen abgelehnt.
  2. A erfüllte versprechen kann durch zurückkehr einen Wert zur onRejected Funktion entweder das .then Verfahrens oder die .catch Methode aus einer abgelehnten Versprechen erstellt werden.
+0

+1 für die elastische Lösung. Ich hatte mit einem anderen '$ q.defer()' gelöst, das in jedem Fall gelöst wurde, aber mit 'Return'-Klausel denke ich, dass es einfacher ist. – Miquel

+0

sollte es 'angular.forEach' sein –

+0

@ pro.mean Vielen Dank, ich habe die Antwort bearbeitet – georgeawg

Verwandte Themen