17

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

4

3 回答 3

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

在这种情况下,除了将 promise 的数组结果传播到其函数的参数之外,其.spread行为类似。要更改它以使用来自 AngularJS 的 Promise 方法,我们只需要不使用. 这导致以下解决方案:.thenonFulfilled.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​​为它充当了过滤器。如果您忽略错误处理程序,它会自动传播任何错误。这意味着如果任何一个输入承诺被拒绝,结果将被拒绝。如果两个 promise 都成功实现, res 是这些分辨率值的数组。

于 2013-05-01T02:28:20.487 回答
1

我有一些东西要添加到@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),但我认为用法很清楚(这是这个库的优点!)。

于 2015-09-03T09:18:40.940 回答
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};
        });
};
于 2015-11-20T11:07:09.377 回答