¿Cómo agrego un retraso en un bucle de JavaScript?
Me gustaría agregar un retraso/suspensión dentro de un while
bucle:
Lo probé así:
alert('hi');
for(var start = 1; start < 10; start++) {
setTimeout(function () {
alert('hello');
}, 3000);
}
Solo el primer escenario es cierto: después de mostrar alert('hi')
, esperará 3 segundos y luego alert('hello')
se mostrará, pero luego alert('hello')
se repetirá de forma constante.
Lo que me gustaría es que después alert('hello')
se muestre 3 segundos después, alert('hi')
luego debe esperar 3 segundos por segunda vez alert('hello')
y así sucesivamente.
La setTimeout()
función no es bloqueante y volverá inmediatamente. Por lo tanto, su bucle se repetirá muy rápidamente e iniciará activadores de tiempo de espera de 3 segundos uno tras otro en rápida sucesión. Es por eso que las primeras alertas aparecen después de 3 segundos y todas las demás siguen sucesivamente sin demora.
Quizás quieras usar algo como esto en su lugar:
var i = 1; // set your counter to 1
function myLoop() { // create a loop function
setTimeout(function() { // call a 3s setTimeout when the loop is called
console.log('hello'); // your code here
i++; // increment the counter
if (i < 10) { // if the counter < 10, call the loop function
myLoop(); // .. again which will trigger another
} // .. setTimeout()
}, 3000)
}
myLoop(); // start the loop
También puedes mejorarlo usando una función de autoinvocación y pasando el número de iteraciones como argumento:
(function myLoop(i) {
setTimeout(function() {
console.log('hello'); // your code here
if (--i) myLoop(i); // decrement i and call myLoop again if i > 0
}, 3000)
})(10); // pass the number of iterations as an argument
Desde ES7 hay una mejor manera de esperar un bucle:
// Returns a Promise that resolves after "ms" Milliseconds
const timer = ms => new Promise(res => setTimeout(res, ms))
async function load () { // We need to wrap the loop into an async function for this to work
for (var i = 0; i < 3; i++) {
console.log(i);
await timer(3000); // then the created Promise can be awaited
}
}
load();
Cuando el motor llega a la await
pieza, establece un tiempo de espera y detiene la ejecución delasync function
. Luego, cuando se completa el tiempo de espera, la ejecución continúa en ese punto. Esto es bastante útil ya que puedes retrasar (1) bucles anidados, (2) condicionalmente, (3) funciones anidadas:
async function task(i) { // 3
await timer(1000);
console.log(`Task ${i} done!`);
}
async function main() {
for(let i = 0; i < 100; i+= 10) {
for(let j = 0; j < 10; j++) { // 1
if(j % 2) { // 2
await task(i + j);
}
}
}
}
main();
function timer(ms) { return new Promise(res => setTimeout(res, ms)); }
Referencia en MDN
Si bien ES7 ahora es compatible con NodeJS y los navegadores modernos, es posible que desee transpilarlo con BabelJS para que se ejecute en todas partes.
Si usa ES6, puede usar un bucle for para lograr esto:
for (let i = 1; i < 10; i++) {
setTimeout(function timer() {
console.log("hello world");
}, i * 3000);
}
Declara i
para cada iteración , lo que significa que el tiempo de espera es el que era antes de + 1000. De esta manera, lo que se pasa setTimeout
es exactamente lo que queremos.