2016-01-19 9 views
5

Sto provando ad usare $ q.all per aspettare che tutte le promesse siano risolte ma si chiama dopo che la prima promessa è finita!

Cosa sto facendo male?

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

Analizzando un po 'più profondo il problema, questa situazione si verifica quando alcune delle risposte sono errore. Per esempio, quando il server restituisce header("HTTP/1.0 418 I'm A Teapot: " . $filename);, console client sarebbe come:

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

Cosa vedi nella console? – yeouuu

+0

La risposta per la prima chiamata a saveAudit: prima chiamata $ http (i controlli hanno 4 elementi). La cosa strana è che i progressi vengono sollevati 4 volte in "finalmente" (uno per ogni audit). – Miquel

risposta

2

La documentazione angolare non va nei dettagli, ma credo che $q.all() si comporta in questo caso allo stesso modo come l'es2015 Promise.all():

Se una delle promesse passate viene respinta, la Promessa del tutto respinge immediatamente con il valore della promessa che ha respinto, scartando tutte le altre promesse che siano state o meno risolte.

Molto probabilmente quello che sta accadendo qui è che almeno una delle vostre richieste sta fallendo. Le istruzioni del tuo log non distinguono se lo $q.all() è riuscito o meno, ma se fallisce, tutto quello che vedrai è il primo errore.

Vedere https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all per la fonte della citazione.

Edit:

Se si desidera ottenere tutte le risposte, anche se alcuni non riescono, allora si dovrebbe aggiungere un gestore catch in saveAudit per convertire i fallimenti in risposte di successo:

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

e quindi è necessario controllare ogni risposta per vedere se contiene un errore o dati validi.

+0

Grazie, questo è quello che ho appena notato (vedi modifica nella domanda). Conosci qualche modo per aggirare questo comportamento? Inoltre, ho visto che se tutte le promesse vengono respinte, la richiamata catch viene chiamata solo una volta, non con tutti gli errori. Almeno uno si aspetterebbe che la chiamata all'errore venga richiamata con tutti gli errori, non solo con il primo ... – Miquel

+0

Se si vuole ottenere tutto l'errore è necessario convertirli in successi. Vedi modifica. – Duncan

2

Come notato da altri, $q.all è non resiliente. Se una delle promesse viene rifiutata, lo $q.all viene rifiutato con il primo errore.

Per creare un resiliente promessa composito, che è una promessa che attende tutte le promesse per completare passaggio o fallire, utilizzare .catch su ogni singola promessa di convertire la promessa respinto ad una promessa di successo.

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

Le due cose da portare via da questa risposta:

  1. Un $q.all promessa è non resilienti. È respinto con la prima promessa respinta.
  2. Una promessa soddisfatta può essere creata da una promessa respinta da restituendo un valore alla funzione onRejected del metodo .then o del metodo .catch.
+0

+1 per la soluzione resiliente. Avevo risolto con un altro '$ q.defer()' che è stato risolto in ogni caso, ma con la clausola 'return' penso sia più semplice. – Miquel

+0

dovrebbe essere 'angular.forEach' –

+0

@ pro.mean Grazie, ho modificato la risposta – georgeawg