Как выполнить Async forEach по списку и каждому элементу, выполнить задачу синхронизации [duplicate]

Если вы используете yml и Twig для переводов в Symfony и хотите использовать многострочные переводы в Javascript, возврат каретки добавляется сразу после перевода. Таким образом, даже следующий код:

var javascriptVariable = "{{- 'key'|trans -}}";

Который имеет следующий перевод yml:

key: >
    This is a
    multi line 
    translation.

По-прежнему будет выглядеть следующий код в html:

var javascriptVariable = "This is a multi line translation.
";

Итак, знак минус в Twig не решает этого. Решение состоит в том, чтобы добавить этот знак минуса после знака в yml:

key: >-
    This is a
    multi line 
    translation.

Будет иметь правильный результат, многострочный перевод на одной строке в Twig:

var javascriptVariable = "This is a multi line translation.";
13
задан Matt Way 3 July 2015 в 03:31
поделиться

6 ответов

Перед ES2017 и async/await (см. ниже для опции в ES2017), вы не можете использовать .forEach(), если хотите дождаться обещания, потому что обещания не блокируются. Javascript и обещания просто не работают.

  1. Вы можете связать несколько обещаний и сделать их инфраструктуру последовательности.
  2. Вы можете выполнять итерацию вручную и продвигать только итерацию когда предыдущее обещание заканчивается.
  3. Вы можете использовать библиотеку, подобную async или Bluebird, которая будет их упорядочивать для вас.

Существует множество различных альтернатив , но .forEach() не сделает этого для вас.


Вот пример последовательности, использующей цепочку обещаний с угловыми обещаниями (если objects - массив):

objects.reduce(function(p, val) {
    return p.then(function() {
        return doSomething(val);
    });
}, $q.when(true)).then(function(finalResult) {
    // done here
}, function(err) {
    // error here
});

И, используя стандартные обещания ES6, это будет:

objects.reduce(function(p, val) {
    return p.then(function() {
        return doSomething(val);
    });
}, Promise.resolve()).then(function(finalResult) {
    // done here
}, function(err) {
    // error here
});

Вот пример ручного секвенирования (если objects - массив), хотя это не сообщает завершение или ошибки, подобные описанному выше:

function run(objects) {
    var cntr = 0;

    function next() {
        if (cntr < objects.length) {
            doSomething(objects[cntr++]).then(next);
        }
    }
    next();
}

ES2017

В ES2017 функция async/wait позволяет вам «ждать» обещания выполнить перед продолжением итерации цикла при использовании не-функциональных циклов, таких как for или while:

[f 4]

Код должен содержаться внутри функции async, а затем вы можете использовать await, чтобы сообщить интерпретатору подождать, пока обещание будет разрешено до продолжения цикла. Обратите внимание, что, хотя это похоже на поведение типа «блокировка», оно не блокирует цикл события. Другие события в цикле событий все еще могут обрабатываться во время await.

19
ответ дан jfriend00 20 August 2018 в 17:45
поделиться
  • 1
    Ваш & quot; уменьшить & quot; решение элегантно, но оно не совсем работает. $q.defer().resolve() разрешает обещание, но возвращает неопределенный, поэтому p не определено для первого элемента. – AgDude 1 December 2015 в 20:03
  • 2
    @AgDude. Если вы знаете, что предложить, предложите изменить мой ответ. Я не соглашаюсь со всеми различными собственными библиотеками обещаний (когда для всего этого есть стандарт ES6), поэтому, если это не правильный способ создания разрешенных обещаний, то для запуска цепочки, я не буду конечно, что это должно быть .. – jfriend00 1 December 2015 в 23:32
  • 3
    элегантное и чистое решение – refactor 27 January 2016 в 12:27
  • 4
    начальное значение $q.dever().resolve() не работало для меня (Угловое 1.4.3), p было undefined при первом вызове. Но это сработало: $q(function(resolve) { resolve();}) – artkoenig 12 March 2016 в 16:21
  • 5
    @Artjom. В Angular 1.5.1, doc говорит, что .resolve() возвращает обещание. Возможно, это было добавлено после 1.4.3? В любом случае, ваш метод работает отлично. Кроме того, он должен быть $q.defer().resolve() - вы пишете его по-разному в своем комментарии. Я думаю, вы также можете использовать $q.when(true). – jfriend00 12 March 2016 в 17:15

Да, вы можете использовать angular.forEach для достижения этого.

Вот пример (если objects - массив):

// Define the initial promise
var sequence = $q.defer();
sequence.resolve();
sequence = sequence.promise;

angular.forEach(objects, function(val,key){
    sequence = sequence.then(function() {
        return doSomething(val);
    });
});

как это можно сделать, используя array.reduce, как и ответ @ friend00 (если objects - массив):

objects.reduce(function(p, val) {
    // The initial promise object
    if(p.then === undefined) {
        p.resolve(); 
        p = p.promise;
    }
    return p.then(function() {
        return doSomething(val);
    });
}, $q.defer());
8
ответ дан Bjarni 20 August 2018 в 17:45
поделиться
  • 1
    вы, сэр, очень правы! это должен быть принятый ответ. вы МОЖЕТЕ использовать угловое. Для достижения этого. – bokkie 7 May 2017 в 19:11

проверить $ q на угловом:

function outerFunction() {

  var defer = $q.defer();
  var promises = [];

  function lastTask(){
      writeSome('finish').then( function(){
          defer.resolve();
      });
  }

  angular.forEach( $scope.testArray, function(value){
      promises.push(writeSome(value));
  });

  $q.all(promises).then(lastTask);

  return defer;
}
4
ответ дан bln 20 August 2018 в 17:45
поделиться
  • 1
    Мне кажется, что он выполняет все параллельные операции и использует q.all(), чтобы знать, когда все будет сделано. Я, хотя ОП спросил, как последовательно выполнять операции по одному. – jfriend00 11 March 2015 в 11:10

Это может помочь кому-то, если я попробовал несколько вышеперечисленных решений, прежде чем придумать свой собственный, который действительно работал для меня (другие не сделали этого)

  var sequence;
  objects.forEach(function(item) {
     if(sequence === undefined){
          sequence = doSomethingThatReturnsAPromise(item)
          }else{
          sequence = sequence.then(function(){
               return doSomethingThatReturnsAPromise(item)
                     }); 
                 }
        });
0
ответ дан Lomithrani 20 August 2018 в 17:45
поделиться

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

var delayedFORLoop = function (array) {
    var defer = $q.defer();

    var loop = function (count) {
        var item = array[count];

        // Example of a promise to wait for
        myService.DoCalculation(item).then(function (response) {

        }).finally(function () {
          // Resolve or continue with loop
            if (count === array.length) {
                defer.resolve();
            } else {
                loop(++count);
            }
        });
    }

    loop(0); // Start loop
    return defer.promise;
}

// To use:
delayedFORLoop(array).then(function(response) {
    // Do something
});

Пример также доступен на моем GitHub: https: //github.com/pietervw/Deferred-Angular-FOR-Loop-Example

2
ответ дан PeterPan 20 August 2018 в 17:45
поделиться

Это сработало для меня так. Я не знаю, подходит ли это подход, но может помочь уменьшить линии

function myFun(){
     var deffer = $q.defer();
     angular.forEach(array,function(a,i) { 
          Service.method(a.id).then(function(res) { 
               console.log(res); 
               if(i == array.length-1) { 
                      deffer.resolve(res); 
               } 
          }); 
     });
     return deffer.promise;
}

myFun().then(function(res){
     //res here
});
0
ответ дан Ricardo Rocha 20 August 2018 в 17:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: