Ejecute la función setInterval sin demora la primera vez

340

Existe una manera de configurar el setIntervalmétodo de javascript para ejecutar el método de inmediato y luego se ejecuta con el temporizador

Jorge
fuente
44
Aunque no de forma nativa. Puede intentar llamar functionuna vez y luego hacer elsetInterval()
Mrchief

Respuestas:

519

Es más simple llamar a la función usted mismo directamente la primera vez:

foo();
setInterval(foo, delay);

Sin embargo, hay buenas razones para evitar setInterval: en particular, en algunas circunstancias, una carga completa de setIntervaleventos puede llegar inmediatamente uno después del otro sin demora. Otra razón es que si desea detener el ciclo, debe llamar explícitamente, lo clearIntervalque significa que debe recordar el identificador devuelto por la setIntervalllamada original .

Entonces, un método alternativo es tener un foodesencadenante para llamadas posteriores usando en su setTimeoutlugar:

function foo() {
   // do stuff
   // ...

   // and schedule a repeat
   setTimeout(foo, delay);
}

// start the cycle
foo();

Esto garantiza que haya al menos un intervalo delayentre llamadas. También hace que sea más fácil cancelar el ciclo si es necesario: simplemente no llama setTimeoutcuando se alcanza la condición de finalización del ciclo.

Mejor aún, puede envolver todo en una expresión de función invocada de inmediato que crea la función, que luego se llama a sí misma como se indicó anteriormente, y automáticamente inicia el ciclo:

(function foo() {
    ...
    setTimeout(foo, delay);
})();

que define la función y comienza el ciclo todo de una vez.

Alnitak
fuente
55
¿Por qué lo prefieres setTimeout?
Sanghyun Lee
19
@Sangdol porque garantiza que los eventos del temporizador no se "acumulen" si no se procesan. En algunas circunstancias, una carga completa de setIntervaleventos puede llegar inmediatamente uno después del otro sin demora.
Alnitak
8
Debería haber algún tipo de prueba que no te permita publicar en la pila cuando estás cansado
James
3
¿Cómo detengo la función si uso el setTimeoutmétodo?
Gaurav Bhor
66
@DaveMunger no, porque no es realmente recursivo, es solo "pseudo-recursivo". Las llamadas "recursivas" no ocurren hasta que el navegador vuelve al bucle de eventos, momento en el cual la pila de llamadas se ha desenrollado por completo.
Alnitak
210

No estoy seguro si te estoy entendiendo correctamente, pero podrías hacer algo como esto fácilmente:

setInterval(function hello() {
  console.log('world');
  return hello;
}(), 5000);

Obviamente, hay varias formas de hacerlo, pero esa es la forma más concisa en la que puedo pensar.

chjj
fuente
16
Esta es una respuesta genial porque es una función con nombre que se ejecuta inmediatamente y también se devuelve. Exactamente lo que estaba buscando.
jmort253
41
Definitivamente es una solución genial, pero es probable que cause confusión para las personas que leen en el futuro.
Deepak Joy
44
No es necesario que le des un nombre "hola". En su lugar, podría devolver argumentos.callee y tener lo mismo con una función anónima
JochenJung
10
@JochenJung arguments.calleeno está disponible en el modo estricto ES5
Alnitak
3
Este es el tipo de código Javascript que confundirá a la mayoría de los nuevos programadores de JS que vienen de otros idiomas. Escriba un montón de cosas como esta si su empresa no tiene mucho personal de JS y desea seguridad laboral.
Ian
13

Me topé con esta pregunta debido al mismo problema, pero ninguna de las respuestas ayuda si necesita comportarse exactamente igual, setInterval()pero con la única diferencia de que la función se llama inmediatamente al principio.

Aquí está mi solución a este problema:

function setIntervalImmediately(func, interval) {
  func();
  return setInterval(func, interval);
}

La ventaja de esta solución:

  • el código existente usando setIntervalpuede adaptarse fácilmente mediante sustitución
  • funciona en modo estricto
  • funciona con funciones y cierres con nombre existentes
  • aún puede usar el valor de retorno y pasarlo más clearInterval()tarde

Ejemplo:

// create 1 second interval with immediate execution
var myInterval = setIntervalImmediately( _ => {
        console.log('hello');
    }, 1000);

// clear interval after 4.5 seconds
setTimeout( _ => {
        clearInterval(myInterval);
    }, 4500);

Para ser descarado, si realmente necesita usar setInterval, también podría reemplazar el original setInterval. Por lo tanto, no se requiere un cambio de código al agregar esto antes de su código existente:

var setIntervalOrig = setInterval;

setInterval = function(func, interval) {
    func();
    return setIntervalOrig(func, interval);
}

Aún así, todas las ventajas enumeradas anteriormente se aplican aquí, pero no es necesaria ninguna sustitución.

Jens Wirth
fuente
Prefiero esta solución sobre la setTimeoutsolución porque devuelve un setIntervalobjeto. Para evitar llamar a funciones que pueden estar más adelante en el código y, por lo tanto, no definidas en el momento actual, envuelvo la primera llamada de función en una setTimeoutfunción como esta: setTimeout(function(){ func();},0);la primera función se llama después del ciclo de procesamiento actual, que también es inmediato , pero es Más error probado.
pensan 02 de
8

Podría incluir setInterval()una función que proporcione ese comportamiento:

function instantGratification( fn, delay ) {
    fn();
    setInterval( fn, delay );
}

... luego úsalo así:

instantGratification( function() {
    console.log( 'invoked' );
}, 3000);
usuario113716
fuente
55
Creo que deberías devolver lo que devuelve setInterval. Esto se debe a que de lo contrario no puede usar clearInterval.
Henrik R
5

Aquí hay un contenedor para personalizarlo si lo necesita:

(function() {
    var originalSetInterval = window.setInterval;

    window.setInterval = function(fn, delay, runImmediately) {
        if(runImmediately) fn();
        return originalSetInterval(fn, delay);
    };
})();

Establezca el tercer argumento de setInterval en verdadero y se ejecutará por primera vez inmediatamente después de llamar a setInterval:

setInterval(function() { console.log("hello world"); }, 5000, true);

O omita el tercer argumento y conservará su comportamiento original:

setInterval(function() { console.log("hello world"); }, 5000);

Algunos navegadores admiten argumentos adicionales para setInterval que este contenedor no tiene en cuenta; Creo que estos rara vez se usan, pero tenlo en cuenta si los necesitas.

Mahn
fuente
99
Anular las funciones del navegador nativo es terrible, ya que puede romper otro código coexistente cuando cambian las especificaciones. De hecho, setInterval actualmente tiene más parámetros: developer.mozilla.org/en-US/docs/Web/API/WindowTimers/…
Áxel Costas Pena
2

Para alguien necesita traer el exterior al thisinterior como si fuera una función de flecha.

(function f() {
    this.emit("...");
    setTimeout(f.bind(this), 1000);
}).bind(this)();

Si la producción de basura anterior le molesta, puede hacer un cierre en su lugar.

(that => {
    (function f() {
        that.emit("...");
        setTimeout(f, 1000);
    })();
})(this);

O tal vez considere usar el @autobinddecorador dependiendo de su código.

Константин Ван
fuente
1

Sugeriré llamar a las funciones en la siguiente secuencia

var _timer = setInterval(foo, delay, params);
foo(params)

También puede pasar el _timermensaje al foo, si lo desea clearInterval(_timer)en una determinada condición

var _timer = setInterval(function() { foo(_timer, params) }, delay);
foo(_timer, params);
Jayant Varshney
fuente
¿Podría explicar esto con más profundidad?
Polyducks el
configura el temporizador desde la segunda llamada, por primera vez simplemente llama al fn directamente.
Jayant Varshney
1

Aquí hay una versión simple para principiantes sin todos los problemas. Simplemente declara la función, la llama y luego comienza el intervalo. Eso es.

//Declare your function here
function My_Function(){
  console.log("foo");
}    

//Call the function first
My_Function();

//Set the interval
var interval = window.setInterval( My_Function, 500 );

Polyducks
fuente
1
Eso es exactamente lo que hace la respuesta aceptada en las primeras líneas. ¿Cómo agrega esta respuesta algo nuevo?
Dan Dascalescu
La respuesta aceptada continúa diciendo que no hagas esto. Mi respuesta a la respuesta aceptada explica por qué sigue siendo un método válido. El OP solicita específicamente llamar a la función setInterval en su primera llamada, pero la respuesta aceptada se desvía para hablar sobre los beneficios de setTimeout.
Polyducks el
0

Para resolver este problema, ejecuto la función por primera vez después de que la página se haya cargado.

function foo(){ ... }

window.onload = function() {
   foo();
};

window.setInterval(function()
{
    foo(); 
}, 5000);
dario
fuente
0

Hay un conveniente paquete npm llamado firstInterval (divulgación completa, es mío).

Muchos de los ejemplos aquí no incluyen el manejo de parámetros y el cambio de comportamientos predeterminados de setInterval cualquier proyecto grande es malo. De los documentos:

Este patrón

setInterval(callback, 1000, p1, p2);
callback(p1, p2);

es idéntico a

firstInterval(callback, 1000, p1, p2);

Si eres de la vieja escuela en el navegador y no quieres la dependencia, es un fácil cortar y pegar del código.

jimm101
fuente
0

// YCombinator
function anonymous(fnc) {
  return function() {
    fnc.apply(fnc, arguments);
    return fnc;
  }
}

// Invoking the first time:
setInterval(anonymous(function() {
  console.log("bar");
})(), 4000);

// Not invoking the first time:
setInterval(anonymous(function() {
  console.log("foo");
}), 4000);
// Or simple:
setInterval(function() {
  console.log("baz");
}, 4000);

Ok, esto es muy complejo, así que déjenme ponerlo más simple:

function hello(status ) {    
  console.log('world', ++status.count);
  
  return status;
}

setInterval(hello, 5 * 1000, hello({ count: 0 }));

NSD
fuente
Esto está muy sobre diseñado.
Polyducks el
0

Puede establecer un tiempo de retraso inicial muy pequeño (por ejemplo, 100) y establecerlo en el tiempo de retraso deseado dentro de la función:

var delay = 100;

function foo() {
  console.log("Change initial delay-time to what you want.");
  delay = 12000;
  setTimeout(foo, delay);
}

reo_yamanaka
fuente
-2

Hay un problema con la llamada asincrónica inmediata de su función, porque setTimeout / setInterval estándar tiene un tiempo de espera mínimo de varios milisegundos, incluso si lo configura directamente a 0. Esto se debe a un trabajo específico del navegador.

Un ejemplo de código con un REAL cero retraso que funciona en Chrome, Safari, Opera

function setZeroTimeout(callback) {
var channel = new MessageChannel();
channel.port1.onmessage = callback;
channel.port2.postMessage('');
}

Puedes encontrar más información aquí

Y después de la primera llamada manual, puede crear un intervalo con su función.

Xe-Xe
fuente
-10

en realidad lo más rápido es hacer

interval = setInterval(myFunction(),45000)

esto llamará mi función, y luego lo hará de nuevo cada 45 segundos, lo que es diferente de hacerlo

interval = setInterval(myfunction, 45000)

que no lo llamará, pero solo programarlo

Hertzel Armengol
fuente
¿De dónde has sacado eso?
Ken Sharp
2
Esto funciona solo si myFunction()se devuelve solo. En cambio, modificar cada función que se llamará por setIntervalél es un mejor enfoque para ajustar setIntervaluna vez como lo proponen las otras respuestas.
Jens Wirth