简体   繁体   English

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

[英]Promise API - combining results of 2 asynchronous call

With promise API, how to send two asynchronous request in parallel, and resolve the combined result as the response. 使用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;
        };

now, when I call get() like 现在,当我调用get()之类的时候

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

I need to get the combined result as indicated. 我需要得到所示的综合结果。 How to do this with Angular promise API? 如何使用Angular promise API执行此操作?

As @Matt said, you need to use $q.all , but the usage isn't quite right. 正如@Matt所说,你需要使用$q.all ,但用法并不完全正确。 AngularJS doesn't support .done and .fail and they don't work quite like that anyway in that there's no such thing as a promise for multiple values, instead you just have a promise for an array. AngularJS不支持.done.fail ,但它们的工作方式并不完全相同,因为没有多个值的承诺,而只是对数组有承诺。

If you were writing this using the full Q we would write: 如果你用完整的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
};

In this case, .spread acts like .then except that it spreads the results of an array for a promise out over the arguments of its onFulfilled function. 在这种情况下, .spread作用类似于.spread .then除了它通过onFulfilled函数的参数传播数组的结果。 To change this to use the promise methods from AngularJS we just need to make do without .spread . 要改变它以使用AngularJS中的promise方法,我们只需要在没有.spread情况下.spread This leads to the following solution: 这导致以下解决方案:

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

The beauty of this is that we are freed from handling all the nitty grity of error propagation and storing the partial results because .then acts as a filter. 这样做的好处是,我们从处理错误传播的所有基本事实grity和存储部分的结果,因为释放.then作为一个过滤器。 If you leave out the error handler, it automatically propagates any errors. 如果省略错误处理程序,它会自动传播任何错误。 This means that if either of the input promises are rejected, the result will be rejected. 这意味着如果任何一个输入承诺被拒绝,结果将被拒绝。 If both promises are fulfilled successfully, res is the array of those resolution values. 如果两个promise都成功完成,则res是这些分辨率值的数组。

I have something to add to @ForbesLindesay answer. 我有什么要添加到@ForbesLindesay的答案。

In our case, we wanted partial results: if a request failed (eg. server has an hiccup, we request something deleted by somebody else, etc.), we still want to collect the valid responses, and to report the errors. 在我们的例子中,我们想要部分结果:如果请求失败(例如,服务器有打嗝,我们请求其他人删除某些内容等),我们仍然希望收集有效的响应,并报告错误。

I found out that we need to handle success and failure on each promise, returning a value that will be collected by $q.all . 我发现我们需要处理每个promise的成功和失败,返回一个将由$q.all收集的值。

Here is our code, simplified and made generic ('item'...): 这是我们的代码,简化和制作通用('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);
    });

Note that we use Lodash (_.map, _.flatten, _.reject, _.isUndefined) but I think the usage is quite clear (that's the nice point of this library!). 请注意,我们使用Lodash(_. map,_.flatten,_.reject,_. isUndefined),但我认为用法很清楚(这是这个库的好处!)。

You can use the angular-q-spread library and then use the same code as @ForbesLindesay's first example: 您可以使用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};
        });
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM