Existe-t-il un moyen simple de ralentir l’itération dans un forEach (avec javascript simple)? Par exemple:
var items = document.querySelector('.item');
items.forEach(function(el) {
// do stuff with el and pause before the next el;
});
Ce que vous voulez réaliser est tout à fait possible avec Array#forEach
- bien que vous puissiez le penser différemment. Vous pouvez pas faire une chose comme ceci:
var array = ['some', 'array', 'containing', 'words'];
array.forEach(function (el) {
console.log(el);
wait(1000); // wait 1000 milliseconds
});
console.log('Loop finished.');
... et obtenez le résultat:
some
array // one second later
containing // two seconds later
words // three seconds later
Loop finished. // four seconds later
Il n'y a pas de fonction wait
ou sleep
synchrone dans JavaScript qui bloque tout le code qui la suit.
La seule façon de retarder quelque chose en JavaScript est de ne pas bloquer. Cela signifie qu’il faut utiliser setTimeout
ou l’un de ses parents. Nous pouvons utiliser le second paramètre de la fonction que nous passons à Array#forEach
: il contient l'index de l'élément en cours:
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.');
En utilisant index
, nous pouvons calculer quand la fonction doit être exécutée. Mais nous avons maintenant un problème différent: la console.log('Loop finished.')
est exécutée avant la première itération de la boucle. En effet, setTimout
est non bloquant.
JavaScript définit les délais d'attente dans la boucle, mais il n'attend pas la fin des délais. Il continue simplement à exécuter le code après la forEach
.
Pour gérer cela, nous pouvons utiliser Promise
s. Construisons une chaîne de promesses:
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.');
});
Il existe un excellent article sur Promise
s en conjonction avec forEach
/map
/filter
ici .
Je deviens plus difficile si le tableau peut changer dynamiquement. Dans ce cas, je ne pense pas que Array#forEach
devrait être utilisé. Essayez ceci à la place:
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>
Vous devez utiliser setTimeout pour créer un délai et avoir une implémentation récursive.
Votre exemple devrait ressembler à
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)
Je pense que la récursivité offre la solution la plus simple.
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')));
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
Cela vous donne un contrôle complet sur le moment auquel vous souhaitez accéder à la prochaine itération de la liste.
Tout d'abord, vous devez changer votre code:
var items = document.querySelectorAll('.item'), i;
for (i = 0; i < items.length; ++i) {
// items[i] <--- your element
}
Vous pouvez parcourir facilement les tableaux en JavaScript avec forEach, mais Malheureusement, ce n'est pas si simple avec les résultats d'un querySelectorAll
En savoir plus sur It here
Je peux vous conseiller de lire ceci répondre pour trouver la bonne solution pour dormir
Vous pouvez utiliser les constructeurs async/await
, Promise
, setTimeout()
et for..of
pour exécuter des tâches dans l'ordre, une duration
pouvant être définie avant l'exécution d'une tâche
(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});
};
})();