Подтвердить что ты не робот

API обещаний - объединение результатов двух асинхронных вызовов

С 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}
},
...

Мне нужно получить комбинированный результат, как указано. Как это сделать с API-интерфейсом Angular?

4b9b3361

Ответ 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, нам просто нужно обойтись без .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 действует как фильтр. Если вы оставите обработчик ошибок, он автоматически распространяет любые ошибки. Это означает, что если любой из входных promises отклонен, результат будет отклонен. Если оба promises выполнены успешно, res - это массив этих значений разрешения.

Ответ 2

У меня есть что добавить в ответ @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 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), но я думаю, что использование довольно ясно (что хорошая точка этой библиотеки!).

Ответ 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};
        });
};