Как мне отложить обратный вызов массива, используя async / await в JavaScript? - PullRequest
3 голосов
/ 02 апреля 2019

Я пытаюсь поместить задержку между каждой итерацией в цикле, используя async await.У меня есть вспомогательная функция сна:

const sleep = ms => {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

И это правильно ожидает между каждым циклом:

for (let i = 0; i < 5; i++) {
    console.log('waiting')
    await sleep(1000)
}

Однако, это не ожидание между каждым циклом:

[0, 1, 2, 3, 4].forEach(async () => {
    console.log('waiting')
    await sleep(1000)
});

Как я могу изменить блок кода forEach, чтобы он вел себя как обычный блок цикла for с задержками между каждой итерацией цикла for?

Ответы [ 3 ]

1 голос
/ 02 апреля 2019

Если вы предпочитаете методы, а не циклы (что я обычно делаю просто эстетически), вы можете добавить сторонний модуль, который я поддерживаю, который называется async-af.

Среди прочего, онобеспечивает последовательный асинхронный интерфейс для каждого :

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

AsyncAF([0, 1, 2, 3, 4]).series.forEach(async () => {
  console.log('waiting');
  await sleep(1000);
});
<script src="https://unpkg.com/async-af@7.0.14/index.js"></script>

Конечно, вы также можете просто использовать простой for...of цикл:

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

(async () => {
  for (const _ of [0, 1, 2, 3, 4]) {
    console.log('waiting');
    await sleep(1000);
  }
})();

Что касается , почему Array.prototype.forEach не работает так, как вы ожидаете, возьмите эту чрезмерно упрощенную реализацию ( вот более полнаяодин ):

const forEach = (arr, fn) => {
  for (let i = 0; i < arr.length; i++) {
    // nothing is awaiting this function call
    fn(arr[i], i, arr);
    // i is then synchronously incremented and the next function is called
  }
  // return undefined
};

forEach([0, 1, 2, 3, 4], async () => {
  console.log('waiting');
  await delay(1000);
});

Как видите, Array.prototype.forEach синхронно вызывает данную функцию обратного вызова для каждого элемента.Вот почему вы видите все пять waiting журналов почти сразу.См. этот вопрос для получения дополнительной информации.

1 голос
/ 02 апреля 2019

Прежде всего, for...of - это путь.

Однако, если вы сильно умрете, хотите метод .forEach, вы можете сделать что-то вроде этого:

const sleep = ms => {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

/* Create your own async forEach */
Array.prototype.asyncForEach = async function asyncForEach(callback, ctx){
  const len = this.length;
  for (let i = 0; i < len; i++)
    await callback.call(ctx, this[i], i, this);
};

[0, 1, 2, 3, 4].asyncForEach(async function(n){
  await sleep(1000);
  console.log(n);
});
1 голос
/ 02 апреля 2019

Вы теоретически могли бы создать цепочку обещаний, что немного лучше с reduce, но тот же шаблон можно сделать и с forEach:

[0, 1, 2, 3, 4].reduce(async (previous) => {
  await previous;
  console.log('waiting')
  await sleep(1000)
});

Но ... почему бы просто не использовать цикл for?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...