Promise API -结合2个异步调用的结果

Promise API - combining results of 2 asynchronous call

本文关键字:调用 结果 异步 2个 API 结合 Promise      更新时间:2023-09-26

使用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的承诺API来做到这一点?

正如@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,除了它将promise的数组结果扩展到其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就是这些分辨率值的数组。

我想给@ForbesLindesay的答案添加一些东西。

在我们的例子中,我们想要部分结果:如果请求失败(例如;服务器有一个小问题,我们请求的东西被别人删除了,等等),我们仍然希望收集有效的响应,并报告错误。

我发现我们需要处理每个承诺的成功和失败,返回一个将被$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(_。地图,_。平,_。reject, _.isUndefined),但我认为用法非常清楚(这就是这个库的优点!)。

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