2013-04-30 69 views
17

使用承諾API,如何並行發送兩個異步請求,並將組合結果作爲響應進行解析。Promise API - 結合2個異步調用的結果

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

現在,當我調用get()之類

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

我需要得到的綜合結果所示。如何使用Angular promise API做到這一點?

回答

36

正如@Matt所說,你需要使用$q.all,但是用法不太正確。 AngularJS不支持.done.fail,它們的工作方式並不像這樣,因爲沒有多個值的承諾,而只是對數組有承諾。

如果你正在寫這個使用全Q可以這樣寫:

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

在這種情況下,.spread作用類似.then,除了它傳播一個數組的結果超過的論點承諾了其onFulfilled功能。要改變這一點以使用AngularJS的承諾方法,我們只需要在沒有.spread的情況下做。這導致了以下解決方案:

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

這樣做的好處是,我們從處理錯誤傳播的所有基本事實grity和存儲部分結果釋放,因爲.then充當過濾器。如果省略錯誤處理程序,它會自動傳播任何錯誤。這意味着如果任一輸入承諾被拒絕,結果將被拒絕。如果兩個promise都成功完成,則res是這些分辨率值的數組。

+0

大答案+1。也感謝您澄清錯誤處理細節,因爲我不確定。如果你有一個時刻,請通知http://stackoverflow.com/questions/16311803/chaining-2-asynchronous-calls-promise-api-to-run-serially,我努力爭取當時的條款不阻止 – bsr 2013-05-01 02:33:26

1

我有一些要添加到@ForbesLindesay答案。我們希望獲得部分結果:如果請求失敗(例如,服務器發生故障,我們請求其他人刪除某些內容等),我們仍然希望收集有效的響應,並報告錯誤。

我發現我們需要處理每個承諾上的成功和失敗,並返回一個將由$q.all收集的值。

這裏是我們的代碼,簡化和通用( '項目' ...):

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

注意,我們使用Lodash(_.map,_.flatten,_.reject,_.isUndefined ),但我認爲這個用法很清楚(這是這個庫的好處!)。

+1

你似乎在那裏有一些字符串文字語法錯誤 – Bergi 2015-09-03 14:07:51

+0

修正了,謝謝你的提升。這就是我手動編輯代碼而沒有語法突出或暗示的地方。太被托架寵壞了...:-) – PhiLho 2015-09-03 14:29:41

+1

謝謝:-)現在代碼是可讀的,我有點關心使用'scope.errorList'。它可能適用於這種特殊情況,並且在鏈的末尾,但它不應該用於通用方法imo。而是從錯誤處理程序(而不是'undefined's)返回'專用值,您可以在最終處理程序中將__filter設置爲'。 – Bergi 2015-09-03 14:35:36

0

您可以使用angular-q-spread庫,然後使用相同的代碼爲@ ForbesLindesay的第一個例子:

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