【问题标题】:Promise API - combining results of 2 asynchronous callPromise API - 结合 2 个异步调用的结果
【发布时间】:2013-04-24 19:57:13
【问题描述】:

使用promise API,如何并行发送两个异步请求,并将合并的结果解析为响应。

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 做到这一点?

【问题讨论】:

    标签: javascript angularjs promise


    【解决方案1】:

    正如@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 的 promise 方法,我们只需要不使用.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
    };
    

    这样做的好处是我们可以从处理错误传播的所有细节和存储部分结果中解脱出来,因​​为.then 充当过滤器。如果您忽略错误处理程序,它会自动传播任何错误。这意味着如果任何一个输入承诺被拒绝,结果将被拒绝。如果两个承诺都成功实现,则 res 是这些分辨率值的数组。

    【讨论】:

    • 很好的答案 +1。还要感谢您澄清错误处理细节,因为我不确定。如果您有时间,请在stackoverflow.com/questions/16311803/… 上提出建议,我很难确保 then 子句不会阻塞
    【解决方案2】:

    我有一些东西要添加到@ForbesLindesay 的答案中。

    在我们的例子中,我们想要部分结果:如果请求失败(例如,服务器出现故障,我们请求被其他人删除的内容等),我们仍然希望收集有效响应并报告错误.

    我发现我们需要处理每个 promise 的成功和失败,返回一个将由$q.all 收集的值。

    这是我们的代码,简化并通用化('item'...):

    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),但我认为用法非常明确(这是这个库的优点!)。

    【讨论】:

    • 那里似乎有一些字符串文字语法错误
    • 已修复,感谢您的提醒。这就是我在没有语法突出显示或提示的情况下手动编辑代码的方式。太被括号宠坏了...... :-)
    • 谢谢 :-) 现在代码可读了,我有点担心scope.errorList 的使用。它可能在这种特殊情况下工作,并且在链的末端,但它不应该在通用方法 imo 中使用。而是来自错误处理程序的return 专用值(而不是undefineds),您可以在最终处理程序中_.filter
    • 是的,当然。我最初考虑只返回一个错误对象,然后在结果中将对象与数组分开(或者通过错误的特殊属性来区分对象,例如)。我使用上面的方法是因为在我们的例子中 if 是可以的并且更简单,但是避免半全局状态当然更干净。
    【解决方案3】:

    您可以使用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};
            });
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-21
      相关资源
      最近更新 更多