使用 forEach 循环执行每次迭代后添加延迟

IT技术 javascript loops foreach delay pause
2021-02-24 15:32:50

有没有一种简单的方法可以减慢 forEach 中的迭代速度(使用普通的 javascript)?例如:

var items = document.querySelector('.item');

items.forEach(function(el) {
  // do stuff with el and pause before the next el;
});
6个回答

你想要实现的目标是完全可能的Array#forEach——尽管你可能会以不同的方式思考它。不能做这样的事情:

var array = ['some', 'array', 'containing', 'words'];
array.forEach(function (el) {
  console.log(el);
  wait(1000); // wait 1000 milliseconds
});
console.log('Loop finished.');

...并获得输出:

some
array          // one second later
containing     // two seconds later
words          // three seconds later
Loop finished. // four seconds later

JavaScript 中没有同步waitsleep函数会阻止其后的所有代码。

在 JavaScript 中延迟某些事情的唯一方法是以非阻塞方式。这意味着使用setTimeout或其亲属之一。我们可以使用传递给函数的第二个参数Array#forEach:它包含当前元素的索引:

var array = ['some', 'array', 'containing', 'words'];
var interval = 1000; // how much time should the delay between two iterations be (in milliseconds)?
array.forEach(function (el, index) {
  setTimeout(function () {
    console.log(el);
  }, index * interval);
});
console.log('Loop finished.');

使用index,我们可以计算何时应该执行函数。但是现在我们有一个不同的问题:在循环的第一次迭代之前console.log('Loop finished.')执行那是因为是非阻塞的。setTimout

JavaScript 在循环中设置超时,但它不会等待超时完成。它只是在forEach.

为了解决这个问题,我们可以使用Promises。让我们构建一个Promise链:

var array = ['some', 'array', 'containing', 'words'];
var interval = 1000; // how much time should the delay between two iterations be (in milliseconds)?
var promise = Promise.resolve();
array.forEach(function (el) {
  promise = promise.then(function () {
    console.log(el);
    return new Promise(function (resolve) {
      setTimeout(resolve, interval);
    });
  });
});

promise.then(function () {
  console.log('Loop finished.');
});

有一个关于一个很好的文章Promise结合与S forEach/ map/filter 这里


如果数组可以动态更改,我会变得更加棘手。在那种情况下,我认为不Array#forEach应该使用。试试这个:

仍然有效,能够减慢调用客户端 API 的 foreach 的速度,该 API 抱怨调用的速度有多快。
2021-04-19 15:32:50
不应该执行通过函数传递的解析吗?: setTimeout(resolve(), interval); 而不是: setTimeout(resolve, interval);
2021-04-23 15:32:50
可能,但更复杂。
2021-04-28 15:32:50
这很好用。现在,假设服务器在 promise 循环运行时向数组添加元素。有没有一种简单的方法 - 在循环内 - 查询和附加数组?假设它是 var array = document.querySelectorAll('.all-at-the-moment-but-stay-tuned‌ ');
2021-04-29 15:32:50

您需要使用 setTimeout 来创建延迟并进行递归实现

你的例子应该看起来像

var items = ['a', 'b', 'c']
var i = 0;
(function loopIt(i) {
  setTimeout(function(){
      // your code handling here
      console.log(items[i]);
      if(i < items.length - 1)  loopIt(i+1)
    }, 2000);
})(i)

@DavidL.Walsh 我的错误,你是对的。我编辑了我的答案
2021-04-20 15:32:50
这只是并行初始化所有超时。它们或多或少会同时执行。此外,内部封闭没有实现任何目标。
2021-05-09 15:32:50

我认为递归提供了最简单的解决方案。

function slowIterate(arr) {
  if (arr.length === 0) {
    return;
  }
  console.log(arr[0]); // <-- replace with your custom code 
  setTimeout(() => {
    slowIterate(arr.slice(1));
  }, 1000); // <-- replace with your desired delay (in milliseconds) 
}

slowIterate(Array.from(document.querySelector('.item')));

您可以使用async/awaitPromise构造函数setTimeout()for..of循环来按顺序执行任务,其中 aduration可以在执行任务之前设置

(async() => {

  const items = [{
    prop: "a",
    delay: Math.floor(Math.random() * 1001)
  }, {
    prop: "b",
    delay: 2500
  }, {
    prop: "c",
    delay: 1200
  }];

  const fx = ({prop, delay}) =>
    new Promise(resolve => setTimeout(resolve, delay, prop)) // delay
    .then(data => console.log(data)) // do stuff

  for (let {prop, delay} of items) {
    // do stuff with el and pause before the next el;
    let curr = await fx({prop, delay});
  };
})();

@KirkRoss 注意,//delay, // do stuffat 代码的顺序可以调整为//do stuff,// delay
2021-04-22 15:32:50

使用 JS Promises 和asnyc/await语法,您可以制作一个sleep真正有效函数。但是,forEach同步调用每个迭代,因此您会得到 1 秒的延迟,然后是所有项目。

const items = ["abc", "def", "ghi", "jkl"];

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

items.forEach(async (item) => {
  await sleep(1000);
  console.log(item);
});

我们可以做的是使用setIntervaland clearInterval(或setTimeout但我们使用前者)来制作定时 forEach 循环,如下所示:

function forEachWithDelay(array, callback, delay) {
  let i = 0;
  let interval = setInterval(() => {
    callback(array[i], i, array);
    if (++i === array.length) clearInterval(interval);
  }, delay);
}

const items = ["abc", "def", "ghi", "jkl"];

forEachWithDelay(items, (item, i) => console.log(`#${i}: ${item}`), 1000);