2013-04-30 15 views
17

Z obiecującym API, jak wysłać dwa asynchroniczne żądania równolegle i rozwiązać połączony wynik jako odpowiedź.Promise API - łączenie wyników 2 asynchronicznego wywołania

var get = function(id){ 
      var res1, res2; 
      var deferred = $q.defer(); 
      Db.get(id, "abc") 
       .then(function (d) { 
        //deferred.resolve(d)); 
        res1 = d; 
       }, function (e) { 
        //error 
       }); 

      Db.get(id, "def") 
       .then(function (d) { 
        //deferred.resolve(d)); 
        res2 = d; 
       }, function (e) { 
        //error 
       }); 

      //?????? how to return {res1:res1 , res2: res2} 

      return deferred.promise; 
     }; 

się, gdy zgłoszę GET() jak

get(123).then(function(d)){ 
// d= {res1: res1, res2: res2} 
}, 
... 

potrzebne, aby uzyskać efekt połączony, jak wskazano. Jak to zrobić z interfejsem Angular Promise API?

Odpowiedz

36

Jak powiedział @Matt, należy użyć $q.all, ale użycie nie jest prawidłowe. AngularJS nie obsługuje .done i .fail i nie działają one tak bardzo, ponieważ nie ma czegoś takiego jak obietnica dla wielu wartości, zamiast tego masz obietnicę dla tablicy.

Jeśli pisali to za pomocą pełnego Q chcielibyśmy napisać:

var get = function (id) { 
    return Q.all([Db.get(id, "abc"), Db.get(id, "def")]) 
     .spread(function (res1, res2) { 
      return {res1: res1, res2: res2}; 
     });//the error case is handled automatically 
}; 

w tym przypadku .spread działa jak .then wyjątkiem tego, że rozprzestrzenia się wyniki tablicy za obietnicę nad argumentami jego Funkcja onFulfilled. Aby zmienić to, aby korzystać z obiecanych metod z AngularJS, musimy po prostu zrobić to bez .spread. Prowadzi to do następującego rozwiązania:

var get = function (id) { 
    return $q.all([Db.get(id, "abc"), Db.get(id, "def")]) 
     .then(function (res) { 
      return {res1: res[0], res2: res[1]}; 
     });//the error case is handled automatically 
}; 

Piękno jest to, że jesteśmy wolni od obsługi wszystkich grity Nitty propagacji błędu i przechowywanie częściowe rezultaty, ponieważ .then działa jak filtr. Jeśli pominąć procedurę obsługi błędów, automatycznie propaguje wszelkie błędy. Oznacza to, że jeśli którakolwiek z obietnic wejściowych zostanie odrzucona, wynik zostanie odrzucony. Jeśli obie obietnice są spełnione, res jest tablicą tych wartości rozdzielczości.

+0

Wielką odpowiedź +1. Dziękuję również za wyjaśnienie szczegółów obsługi błędów, ponieważ nie byłem pewien. Jeśli masz chwilę, proszę doradzić na http://stackoverflow.com/questions/16311803/chaining-2-asynchronous-calls-promise-api-to -run-serially, walczę, że klauzula wtedy nie blokuje – bsr

1

Mam coś do dodania do odpowiedzi @ForbesLindesay.

W naszym przypadku chcieliśmy uzyskać częściowe wyniki: jeśli żądanie nie powiodło się (np. Serwer ma czkawkę, prosimy o coś usunięte przez kogoś innego itp.), Nadal chcemy zebrać prawidłowe odpowiedzi i zgłosić błędy.

Dowiedziałem się, że musimy poradzić sobie z sukcesem i porażką przy każdej obietnicy, zwracając wartość, która zostanie zebrana przez $q.all.

Oto nasz kod, uproszczone i rodzajowe ('element' ...):

var promiseList = _.map(itemList, function(item) 
{ 
    return DataService.getISubtems(item.id) 
     .then(
      function(response) 
      { 
       var subItems = response.data; 
       $log.info('Received sub-item list;' + subItems.length + ';items received'); 
       return subItems; 
      }, 
      function(reason) 
      { 
       $log.warn('Sub-item list not received for item;' + item.name + ';' + item.id); 
       $scope.errorList.push('Sub-item list not received for item "' + item.name + '"'); 
      } 
     ); 
}); 
$q.all(promiseList) 
    .then(function(itemArray) 
    { 
     // We get an array of arrays interleaved with undefined value when an error was raised. 
     // That's because error handling doesn't return anything, ie. returns undefined. 
     // We remove these undefined values then put all operations at the same level. 
     var allOperations = _(operationArray).reject(_.isUndefined).flatten().value(); 
     if ($scope.errorList.length > 0) 
     { 
      NotificationService.warning('Items Fetching', 'Errors while getting item list:\n' + 
       $scope.errorList.join('\n')); 
     } 
     $scope._onItemListReceived(allItems); 
    }); 

Zauważ, że używamy Lodash (_.map, _.flatten, _.reject, _.isUndefined), ale myślę, że użycie jest całkiem jasne (to ładny punkt tej biblioteki!).

+1

Wygląda na to, że masz tutaj kilka literowych błędów składniowych, – Bergi

+0

Naprawiono, dziękuję za te heads up. To właśnie dostaję do ręcznej edycji kodu bez podświetlania składni ani podpowiedzi. Zbyt zniszczony przez nawiasy ...:-) – PhiLho

+1

Dzięki :-) Teraz, gdy kod jest czytelny, jestem nieco zaniepokojony użyciem 'scope.errorList'. Może działać w tym konkretnym przypadku i na końcu łańcucha, ale nie powinno się go używać w ogólnej metodzie imo. Zamiast tego zwracaj dedykowane wartości z obsługi błędu (zamiast 'undefined's), że możesz' _.filter' włączyć w ostatecznym module obsługi. – Bergi

0

Można użyć biblioteki angular-q-spread a następnie użyć tego samego kodu jako użytkownika @ ForbesLindesay pierwszym przykładzie:

// The module needs $q-spread as a dependency: 
// angular.module('…', ['$q-spread']); 

var get = function (id) { 
    return $q.all([Db.get(id, "abc"), Db.get(id, "def")]) 
     .spread(function (res1, res2) { 
      return {res1: res1, res2: res2}; 
     }); 
}; 
Powiązane problemy