2016-01-19 26 views
5

Próbuję użyć $ q.all, aby poczekać, aż wszystkie obietnice zostaną rozwiązane, ale jest to wywoływane po zakończeniu pierwszej obietnicy!

Co robię źle?

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

Analizując trochę głębiej problemu, taka sytuacja występuje, gdy niektóre odpowiedzi są błędem. Na przykład, gdy serwer zwraca header("HTTP/1.0 418 I'm A Teapot: " . $filename); konsoli klient będzie jak:

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

Co widzisz na konsoli? – yeouuu

+0

Odpowiedź dla pierwszego połączenia na saveAudit: pierwsze wywołanie $ http (audyty mają 4 elementy). Dziwne jest to, że postęp jest podnoszony 4 razy w "końcu" (po jednym dla każdej kontroli). – Miquel

Odpowiedz

2

kątowe dokumentacja nie wchodzić w szczegóły, ale wierzę $q.all() zachowuje się w tym przypadku taki sam sposób jak w es2015 Promise.all():

Jeśli którakolwiek z odrzuconych obietnic odrzuci, wszystkie obietnice natychmiast odrzucają wartość odrzuconej obietnicy, odrzucając wszystkie inne obietnice, niezależnie od tego, czy zostały rozwiązane.

Najprawdopodobniej dzieje się tak, że przynajmniej jedna z Twoich próśb się nie udała. Twoje instrukcje dziennika nie rozróżniają, czy $q.all() powiodło się, czy też się nie udało, ale jeśli się nie powiedzie, zobaczysz tylko pierwszy błąd.

Aby uzyskać źródło cytatu, patrz: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all.

Edit:

Jeśli chcesz uzyskać wszystkie odpowiedzi, nawet jeśli niektórzy nie, to należy dodać catch obsługi w saveAudit przekonwertować niepowodzeń w udane odpowiedzi:

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

, a następnie musisz sprawdzić każdą odpowiedź, aby sprawdzić, czy zawiera błąd lub prawidłowe dane.

+0

Dzięki, właśnie to zauważyłem (patrz edycja w pytaniu). Czy znasz jakiś sposób na ominięcie tego zachowania? Również widziałem, że jeśli wszystkie obietnice są odrzucane, wywołania zwrotne są wywoływane tylko raz, a nie ze wszystkimi błędami. Przynajmniej jeden mógłby oczekiwać wywołania zwrotnego błędu z wszystkimi błędami, nie tylko z pierwszym ... – Miquel

+0

Jeśli chcesz uzyskać cały błąd, musisz go przekonwertować na sukces. Zobacz edycję. – Duncan

2

Jak zauważyli inni, $q.all jest nie odporny. Jeśli jedna z obietnic zostanie odrzucona, $q.all zostaje odrzucony z pierwszym błędem.

Aby utworzyć sprężysty kompozytowego obietnicę, że jest to obietnica, że ​​czeka na wszystkich obietnic wypełnić podanie lub nie, użyj .catch na poszczególnych obietnicy konwertować odrzuconych obiecują udanej obietnicy.

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

Dwie rzeczy zabrać z tej odpowiedzi:

  1. $q.all obietnica nie sprężysty. Jest odrzucany z pierwszą odrzuconą obietnicą.
  2. Wypełniona obietnica może zostać utworzona z odrzuconej obietnicy przez zwracając wartość dla funkcji OnRejected metody .then lub .catch.
+0

+1 dla odpornego rozwiązania. Rozwiązałem z innym '$ q.defer()', które zostało rozwiązane w każdym przypadku, ale z klauzulą ​​'return', uważam, że jest prostsze. – Miquel

+0

Powinno to być "kątowe. Dla każdego" –

+0

@ pro.mean Dziękuję, zredagowałem odpowiedź – georgeawg

Powiązane problemy