Как использовать $q.all для обработки результата $http.get в нескольких асинхронных функциях

У меня есть массив объектов, возвращаемых из $http.get, для которых мне нужно выполнить три разных автономных почтовых процесса. Я считаю, что могу использовать $q.all для параллельного выполнения почтовых процессов.

Я вижу массу примеров, использующих $timeout/setTimeout, которые следуют этому шаблону:

function doSomethingAsync() {
    var deferred = $q.defer();

    setTimeout(function() {
        deferred.resolve('hello world');
    }, 500);

    return deferred.promise;
}

doSomethingAsync().then(function(val) {
    console.log('Promise Resolved!', val);
});

Я новичок в javascript, но это не суть моей проблемы. Функции $timeout и setTimeout уже являются асинхронными, и поэтому doSomethingAsync просто не возвращает обещание... фактическая работа не выполнялась inline.

В следующем примере я не понимаю, как функция возвращает обещание сразу после выполнения. Будет ли «return deferred.promise» выполняться до завершения цикла? Не будет ли функция вынуждена сначала выполнять цикл, а затем оператор return?

function doSomethingRealWorkAsync() {
    var deferred = $q.defer();

    //loop through the items in an array and perform some heavy lifting that will take time

    deferred.resolve('Done!');
    return deferred.promise;
}

doSomethingRealWorkAsync().then(function(val) {
    console.log('Promise Resolved!', val);
});

В каждом рассмотренном мной примере используется асинхронный процесс (либо $timeout, либо setTimeout) для имитации выполняемой работы. А реальная работа? Что делать, если у меня есть несколько длительных процессов, которые я хочу запустить параллельно?

Использование приведенного выше шаблона даст следующий код, но я не могу представить, чтобы он работал правильно. Будет ли это работать? Если нет, то почему? Если да, то как?

$scope.items = [{}];
$scope.initialized = false;

function doOneThingToItemsAsync() {
    var deferred = $q.defer();

    for (var i in items) {
        items[i].propertyA = 'this';
    }
    deferred.resolve('Done with One Thing!');
    return deferred.promise;
}

function doAnotherThingToItemsAsync() {
    var deferred = $q.defer();

    for (var i in items) {
        items[i].propertyB = 'that';
    }

    deferred.resolve('Done with Another Thing!');
    return deferred.promise;
}

function doYetAnotherThingToItemsAsync() {
    var deferred = $q.defer();

    for (var i in items) {
        items[i].propertyC = 'I smell a cat';
    }

    deferred.resolve('Done with Yet Another Thing!');
    return deferred.promise;
}

function getItems () {
    $http.get("/api/widgets", { timeout: 0 })
        .success(function (data) {
            items = data.items;
            $q.all([doOneThingToItemsAsync(), 
                    doAnotherThingToItemsAsync(),
                    doYetAnotherThingToItemsAsync()])
                .then(function(result) {
                    for (var i in result) {
                        console.log(result[i]);
                    }
                    initialized = true;
                });
        })
        .error(function (data) {
            if (data.errorMessage) {
                console.log("There was a problem retrieving your data: \n" + data.errorMessage + "\nPlease try again.");
            } else {
                console.log("There was a problem retrieving your data.  Please try again.");
            }
        });
}

Оценивает ли интерпретатор браузера тип, возвращаемый функцией (в данном случае обещание), и позволяет ли вам вызывать методы для этого типа (например, затем, успех, ошибка), передавая функции обратного вызова, которые будут выполняться при выполнении deferred.resolve, deferred.reject и т.д.?


person Greg Grater    schedule 08.09.2014    source источник
comment
Может быть, вам нужны WebWorkers, если вам нужно обрабатывать много данных параллельно?   -  person dfsq    schedule 08.09.2014
comment
@dfsq, спасибо за комментарий. Будут ли WebWorkers иметь доступ к угловым переменным и служебным переменным? Поскольку это отдельные файлы js, я думал, что у них ограниченный доступ.   -  person Greg Grater    schedule 08.09.2014


Ответы (2)


Нет, как вы уже поняли, вам нужно убедиться, что обещание возвращено, прежде чем будет выполнена какая-либо фактическая работа. Поэтому выполнение встроенной работы перед возвратом обещания не сработает.

По сути, идея состоит в том, чтобы иметь встроенную функцию, которая только создает отложенный объект и возвращает из него обещание, а затем иметь асинхронную операцию, которая выполняет фактическую работу и имеет доступ к отложенному объекту через замыкание и может установить его разрешение один раз. работа сделана.

Итак, вы можете взять свои примеры и сделать что-то вроде этого:

function doSomethingAsync() {
var deferred = $q.defer();

setTimeout(function() {
   // do the actual work
   deffered.resolve();
}, 500);

return deferred.promise;
}

doSomethingAsync().then(function() {
console.log('Promise Resolved! and work is done');
}); 
person Rickard Staaf    schedule 08.09.2014
comment
Сам $timeout возвращает обещание.. вы можете просто написать return $timeout(function(){ return dataAfterSomeStuffs }); внутри функции - person PSL; 10.09.2014

Будет ли «return deferred.promise» выполняться до завершения цикла? Не будет ли функция вынуждена сначала выполнять цикл, а затем оператор return?

Да. Циклы синхронны.

В каждом рассмотренном мной примере используется асинхронный процесс (либо $timeout, либо setTimeout) для имитации выполняемой работы. А реальная работа? Что делать, если у меня есть несколько длительных процессов, которые я хочу запустить параллельно?

JavaScript не работает параллельно. Действительно тяжелая работа обычно связана с вводом-выводом, и в любом случае это асинхронно. Если вы выполняете интенсивную обработку с помощью самого JS (что может быть не лучшим выбором), вам следует: а) разделить работу на более мелкие фрагменты, которые выполняются последовательно с тайм-аутами (асинхронно), или б) выполнять обработку в отдельной среде. , то есть WebWorker, с асинхронной связью.

Использование приведенного выше шаблона даст следующий код, но я не могу представить, чтобы он работал правильно. Будет ли это работать?

Он будет «работать», но не будет работать асинхронно, не говоря уже о параллельном.

Оценивает ли интерпретатор браузера тип, возвращаемый функцией (в данном случае обещание), и позволяет ли вам вызывать методы для этого типа (например, затем, успех, ошибка), передавая функции обратного вызова, которые будут выполняться при выполнении deferred.resolve, deferred.reject и т.д.?

Никакой магии интерпретатора здесь не происходит. Промисы — это обычные объекты с обычными методами, которые могут быть созданы пользовательскими библиотеками, и к ним применяется обычная семантика.

person Bergi    schedule 10.09.2014