AngularJS - отказоустойчивость на $q.all()

Я пытаюсь заполнить некоторые локальные данные, разрешая серию удаленных вызовов.
Когда каждое обещание разрешено, я загружаю данные и продолжаю.

Метод $q.all( [] ) выполняет именно это:

        $q.all([
            this.getUserInfo(11)
                .then(function (r) {
                    results.push(r)
                }),

            this.getUserConns()
                .then(function (r) {
                    results.push(r)
                }),

            this.getUserCtxs()
                .then(function (r) {
                    results.push(r)
                })
        ])
        .then(function () {
            console.log(results)
        })


Проблема в том, что этот код не является устойчивым.
Если любой из этих вызовов терпит неудачу, никто не получает рыбу!

Обертка вызовов в инструкции try/catch просто заставляет $q.all() полностью игнорировать запись, даже если она не работает (обратите внимание на console.log в func)...

        $q.all([
            this.getUserInfo2(11)
                .then(function (r) {
                    results.push(r)
                }),

            function () {
                try {
                    this.getUserGroups()
                        .then(function (r) {
                            console.log(r)
                            results.push(r)
                        })
                }
                catch (err) {
                    console.log(err)
                }
            },
        ])
        .then(function () {
            console.log(results)
        })

Вывод:

[Объект]


Любой намек на то, как я могу обернуть это, чтобы он был устойчивым?


Благодаря @dtabuenc, я сделал еще один шаг вперед. Внедряя обратный вызов ошибки, я могу избежать разрыва цепи и нажать значения разрешенного promises.

Однако на консоли все еще отображается неприятное исключение... Как я могу избавиться от этого, если я не могу попробовать/поймать асинхронные запросы?

Код вызывающего абонента

    return $q.all([

            this.getUserInfo(user_id)
                .then(function (r) {
                    results['personal_details'] = r
                }),

            this.getUserConns()
                .then(
                    function (r) {
                    results['connections'] = r
                    },
                    function(err) {
                        console.log(err)
                    })

        ])
        .then(function () {
            return (results)
        })

Код Callee (ввод с исключением)

    getUserConns: function() {

        return __doCall( ws.getUserConnections, {} )
            .then( function(r) {

                // very generic exception injected
                throw new Error

                if (r && r.data['return_code'] === 0) {
                    return r.data['entries']
                }
                else {
                    console.log('unable to retrieve the activity - err: '+r.data['return_code'])
                    return null
                }
            })
    },

Ответы

Ответ 1

Это будет работать, но также вытолкнуть ошибки в массив.

function push(r) {
    results.push(r);
}

$q.all([
    this.getUserInfo(11).then(push).catch(push),
    this.getUserConns().then(push).catch(push),
    this.getUserCtxs().then(push).catch(push)
])
.then(function () {
    console.log(results);
})

Вы также должны улучшить свое понимание promises, вы никогда должны использовать try-catch с promises - при использовании promises, вы используете метод .catch() (со всем остальным неявно a try). Это работает как для обычных ошибок, так и для асинхронных ошибок.


Если вы хотите полностью игнорировать ошибки:

function push(r) {
    results.push(r);
}

function noop() {}

$q.all([
    this.getUserInfo(11).then(push).catch(noop),
    this.getUserConns().then(push).catch(noop),
    this.getUserCtxs().then(push).catch(noop)
])
.then(function () {
    console.log(results);
})

Ответ 2

Мне кажется, что это проще сделать:

$q.all([
 mypromise1.$promise.catch(angular.noop),
 mypromise2.$promise.catch(angular.noop),
 mypromise1.$promise.catch(angular.noop)
])
.then(function success(data) {
 //.....
});

Ответ 3

Я не уверен, что вы подразумеваете под устойчивым. Что вы хотите, если один из promises выходит из строя?

Ваш try-catch не будет работать, потому что обещание будет асинхронным.

Однако вы можете передать обработчик ошибок в качестве второго параметра в вызов then() и делать все, что вы пожелаете.

Ответ 4

Такая же проблема. Для тех из вас, у кого есть петли: внутри ответа:

var tracks = [];
var trackDfds = [];
for(var i = 0; i < res.items.length; i++){
    var fn = function () {
        var promise = API.tracks(userId, res.items[i].id);
        return promise.then(function (res) {
            if (res.items.length) {
              tracks.push(res.items);
            }
        }).catch(angular.noop);
    };
    trackDfds.push(fn());
}
$q.all(trackDfds)
    .then(function (res) {
        console.log(tracks);
    });

Ответ 5

@Ответ на Esailija кажется обходным решением проблемы. Вы не можете решить проблему вне основного источника проблемы: $q.

Кажется немного мудрее иметь отклонения обратных вызовов для каждого then (2-й аргумент) и там вставить $q.reject(...).

Пример:

$q.all([
    this.getUserInfo(11).then(
        function (response) { // UI data preparation for this part of the screen }, 
        function (response) {
           $q.reject(response);
        }
    ),
    // ...
])
.then(
    function () {
      // all good
    },
    function () {
      // at least one failed
    }
)

Это особенно заметно, когда модель пользовательского интерфейса зависит от всех вызовов ajax.

Лично я считаю, что это безопасный способ действовать в любом случае, потому что большую часть времени вы хотите направить некоторые серверные сообщения на какой-то компонент тоста на обратные вызовы отклонения или каким-то образом оповестить пользователя (очередь 7 вызовов ajax doesn 't означает, что вы ничего не можете показать, потому что 1 не удалось - это означает, что вы не сможете показать какую-либо область экрана - для этого нужна специальная обратная связь с пользователем).