2013-04-30 65 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}; 
     }); 
};