[英]Add a delay after executing each iteration with forEach loop
有沒有一種簡單的方法可以減慢 forEach 中的迭代速度(使用普通的 javascript)? 例如:
var items = document.querySelector('.item');
items.forEach(function(el) {
// do stuff with el and pause before the next el;
});
使用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>
您需要使用 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)
您可以使用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}); }; })();
我認為遞歸提供了最簡單的解決方案。
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')));
您可以做出承諾並將其與 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}`)
}
使用 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);
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
這使您可以完全控制何時要訪問列表中的下一個迭代。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.