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

问题描述 投票:0回答:9

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

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

items.forEach(function(el) {
  // do stuff with el and pause before the next el;
});
javascript loops foreach delay pause
9个回答
96
投票

您想要实现的目标完全可以通过

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 中没有同步

wait
sleep
函数来阻止其后面的所有代码。

在 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

 之后的代码。

为了解决这个问题,我们可以使用

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

 的优秀文章与 
forEach
/
map
/
filter
 
在这里


如果数组可以动态改变,我会变得更棘手。在这种情况下,我认为不应该使用

Array#forEach

。试试这个:

var array = ['some', 'array', 'containing', 'words']; var interval = 2000; // how much time should the delay between two iterations be (in milliseconds)? var loop = function () { return new Promise(function (outerResolve) { var promise = Promise.resolve(); var i = 0; var next = function () { var el = array[i]; // your code here console.log(el); if (++i < array.length) { promise = promise.then(function () { return new Promise(function (resolve) { setTimeout(function () { resolve(); next(); }, interval); }); }); } else { setTimeout(outerResolve, interval); // or just call outerResolve() if you don't want to wait after the last element } }; next(); }); }; loop().then(function () { console.log('Loop finished.'); }); var input = document.querySelector('input'); document.querySelector('button').addEventListener('click', function () { // add the new item to the array array.push(input.value); input.value = ''; });
<input type="text">
<button>Add to array</button>


9
投票
您需要利用 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)


6
投票
使用 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); });

我们可以做的是使用

setInterval

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);


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

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')));
    

2
投票
您可以使用

async/await

Promise
 构造函数、
setTimeout()
for..of
 循环按顺序执行任务,其中可以在执行任务之前设置 
duration
 设置

(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}); }; })();


2
投票
您可以做出承诺并将其与 for 一起使用,该示例必须位于 async/await 函数中:

let myPromise = () => new Promise((resolve, reject) => { setTimeout(function(){ resolve('Count') }, 1000) }) for (let index = 0; index < 100; index++) { let count = await myPromise() console.log(`${count}: ${index}`) }
    

0
投票
首先你必须更改你的代码:

var items = document.querySelectorAll('.item'), i; for (i = 0; i < items.length; ++i) { // items[i] <--- your element }

您可以在 JavaScript 中使用 forEach 轻松循环数组,但是 不幸的是,从结果来看事情并不是那么简单 查询选择器全部

阅读更多相关信息

这里

我可以建议您阅读此

答案以找到正确的睡眠解决方案


0
投票
有很多复杂的答案,其中大多数并不能真正以简单直接的方式解决这个问题。因此,这里有一个简单但“不愉快”的解决方案:

const timestamp = new Date().getTime() const items = ["1", "2", "3"] items.forEach((item, index) => { console.log(item) // Loop until the index * 1000 milliseconds has passed while (new Date().getTime() < timestamp + index * 1000) {} })
    

-1
投票

发电机

function* elGenLoop (els) { let count = 0; while (count < els.length) { yield els[count++]; } } // This will also work with a NodeList // Such as `const elList = elGenLoop(document.querySelector('.item'));` const elList = elGenLoop(['one', 'two', 'three']); console.log(elList.next().value); // one console.log(elList.next().value); // two console.log(elList.next().value); // three

这使您可以完全控制何时要访问列表中的下一个迭代。

© www.soinside.com 2019 - 2024. All rights reserved.