W3docs

JavaScript setTimeout y setInterval

Aprende a programar código en JavaScript con setTimeout y setInterval: sintaxis, argumentos, cancelación de temporizadores, setTimeout recursivo, comportamiento de retardo cero, debouncing y throttling.

A veces no quieres ejecutar código en este momento — quieres ejecutarlo más tarde, o ejecutarlo repetidamente. Las dos funciones de programación de JavaScript, setTimeout() y setInterval(), te permiten hacer exactamente eso. Esta guía cubre su sintaxis, cómo pasar argumentos, cómo cancelar un temporizador programado, el importante patrón de "setTimeout recursivo", el sorprendente comportamiento de un retardo de cero, y dos usos del mundo real: debouncing y throttling.

Ninguna de las dos funciones forma parte del lenguaje JavaScript central — las proporciona el entorno anfitrión (navegadores y Node.js). El comportamiento descrito aquí es el mismo en ambos, con algunas diferencias señaladas a lo largo del camino.

Introducción a las funciones de temporización de JavaScript

Un temporizador programa un callback para que se ejecute después de que el código actual termine y haya pasado una cantidad de tiempo determinada. Dado que JavaScript es de un solo hilo, el callback nunca interrumpe el código en ejecución; espera en una cola y se ejecuta solo cuando la pila de llamadas está vacía. (Para conocer el mecanismo completo, consulta The Event Loop.)

La función setTimeout()

setTimeout() ejecuta una función una vez después de un retardo especificado. Acepta una función a ejecutar y un retardo en milisegundos antes de esa ejecución.

Sintaxis

let timerId = setTimeout(func, delay, arg1, arg2, ...);
  • func — la función (o, menos comúnmente, una cadena de código) a ejecutar.
  • delay — la espera en milisegundos antes de ejecutar. Por defecto es 0.
  • arg1, arg2, ... — argumentos opcionales que se pasan directamente a func.

El valor de retorno es un id de temporizador numérico que puedes pasar posteriormente a clearTimeout().

Ejemplo

javascript— editable

Pasar argumentos al callback

Todo lo que pongas después del retardo se reenvía al callback. Esto es más limpio que envolver la llamada en otra función flecha:

javascript— editable
Advertencia
Pasa la función en sí misma, no su resultado. setTimeout(greet(), 1000) ejecuta greet() inmediatamente y programa su valor de retorno (probablemente undefined). Escribe setTimeout(greet, 1000) — sin paréntesis.

La función setInterval()

setInterval() tiene la misma firma que setTimeout(), pero en lugar de ejecutar el callback una vez, lo ejecuta repetidamente cada delay milisegundos hasta que lo detengas.

Sintaxis

let timerId = setInterval(func, delay, arg1, arg2, ...);

Ejemplo

javascript— editable
Información
Si el callback tarda más en ejecutarse que el intervalo, las llamadas pueden acumularse y el espaciado real se desvía. El navegador también garantiza una brecha mínima entre callbacks, por lo que las ejecuciones consecutivas pueden estar más cerca del siguiente tick de lo que esperas. Cuando importa el espaciado preciso, prefiere el patrón setTimeout recursivo que se describe a continuación.

setTimeout recursivo vs. setInterval

Puedes reproducir setInterval() haciendo que un callback de setTimeout() se reprograme a sí mismo. La diferencia clave: setInterval() mide el retardo entre inicios, mientras que el setTimeout() recursivo lo mide entre el final de una ejecución y el inicio de la siguiente — garantizando una pausa fija incluso cuando el callback es lento.

javascript— editable

Cancelar la ejecución programada

Ambas funciones devuelven un id de temporizador. Conserva ese id y podrás cancelar el trabajo programado con clearTimeout() o clearInterval(). (Las dos funciones de cancelación son en realidad intercambiables en la mayoría de los motores, pero hacer coincidir cada una con el programador que creó el id mantiene el código legible.)

Detener setTimeout()

Para cancelar un timeout, guarda el id devuelto por setTimeout() y pásalo a clearTimeout() antes de que transcurra el retardo.

Ejemplo

javascript— editable

Detener setInterval()

De manera similar, guarda el id de setInterval() y pásalo a clearInterval(). Sin esto, el intervalo se ejecuta indefinidamente (o hasta que se cierre la página), lo que es una fuente común de fugas de memoria y temporizadores descontrolados.

Ejemplo

javascript— editable

El setTimeout con retardo cero

setTimeout(func, 0) no ejecuta func inmediatamente. Programa func para que se ejecute tan pronto como el código sincrónico actual haya terminado. Esta es una forma práctica de "ceder" — para dejar que el navegador repinte o para dividir una tarea larga en partes — y explica el orden de salida que a menudo sorprende a los principiantes:

javascript— editable

Ten en cuenta que los temporizadores son macrotareas: se ejecutan después de todas las microtareas en cola (como los callbacks de promesas resueltas). Consulta Event loop: microtasks and macrotasks para conocer las reglas de ordenamiento precisas.

Aplicaciones prácticas y consejos

Dos de los usos más comunes del mundo real de setTimeout() son el debouncing y el throttling — ambas son formas de limitar la frecuencia con la que se ejecuta una función en respuesta a eventos rápidos.

Debouncing con setTimeout()

El debouncing espera hasta que una ráfaga de eventos se haya detenido antes de ejecutar la función. Cada nuevo evento reinicia el temporizador, por lo que el callback se activa solo después de que las cosas se calmen durante wait milisegundos. Esto es ideal para un cuadro de búsqueda: quieres enviar una solicitud después de que el usuario deje de escribir, no una por cada pulsación de tecla.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Debounced Input Example</title>
<script>
    // Debounce function to limit the rate at which a function is executed
    function debounce(func, wait) {
        let timeout;

        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };

            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }

    // Function to be debounced
    function fetchData(input) {
        alert(`API call with input: ${input}`); // Placeholder for an API call
    }

    // Create a debounced version of fetchData
    const debouncedFetchData = debounce(fetchData, 300);

    // Add the debounced function to an event listener
    function setup() {
        document.getElementById('searchInput').addEventListener('input', (event) => {
            debouncedFetchData(event.target.value);
        });
    }

    // Ensure setup is called once the document is fully loaded
    document.addEventListener('DOMContentLoaded', setup);
</script>
</head>
<body>
    <h3>Type in the input field:</h3>
    <input type="text" id="searchInput" placeholder="Start typing..." />
</body>
</html>

Throttling con setTimeout()

El throttling ejecuta la función como máximo una vez por limit milisegundos, sin importar cuántos eventos lleguen entretanto. Donde el debouncing espera el silencio, el throttling garantiza un ritmo constante — perfecto para los manejadores de scroll, resize o mousemove que de otro modo dispararían docenas de veces por segundo. El ejemplo a continuación utiliza un enfoque de borde inicial (se ejecuta inmediatamente en el primer evento y luego impone el intervalo):

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Throttled Scroll Event</title>
<style>
  /* Simple styling for demonstration */
  body, html {
    height: 200%; /* Make the page scrollable */
    margin: 0;
    padding: 0;
    font-family: Arial, sans-serif;
  }
  #log {
    position: fixed;
    top: 0;
    left: 0;
    background: white;
    border: 1px solid #ccc;
    padding: 10px;
    width: 300px;
  }
</style>
</head>
<body>
<div id="log">Scroll to see the effect...</div>
<script>
// Throttle function using setTimeout
function throttle(func, limit) {
  let lastFunc;
  let lastRan;
  return function() {
    const context = this;
    const args = arguments;
    if (!lastRan) {
      func.apply(context, args);
      lastRan = Date.now();
    } else {
      clearTimeout(lastFunc);
      lastFunc = setTimeout(function() {
        if ((Date.now() - lastRan) >= limit) {
          func.apply(context, args);
          lastRan = Date.now();
        }
      }, Math.max(0, limit - (Date.now() - lastRan)));
    }
  }
}

// Function to be throttled
function handleScroll() {
  const log = document.getElementById('log');
  log.textContent = `Scroll event triggered at: ${new Date().toLocaleTimeString()}`;
}

// Add event listener for scroll
window.addEventListener('scroll', throttle(handleScroll, 1000));
</script>
</body>
</html>

Problemas a recordar

  • Los retardos son un mínimo, no una garantía. Si la pila de llamadas está ocupada o el bucle de eventos está congestionado, el callback espera. El número que pasas es lo más pronto que puede ejecutarse, no una promesa.
  • Las pestañas en segundo plano están limitadas. La mayoría de los navegadores limitan los temporizadores en pestañas inactivas a aproximadamente una vez por segundo para ahorrar energía, por lo que las animaciones y el polling se ralentizan cuando la pestaña está oculta.
  • Los timeouts anidados están limitados a ~4ms. Después de cinco llamadas anidadas a setTimeout(), los navegadores imponen un retardo mínimo de aproximadamente 4 milisegundos, por lo que un retardo de 0 nunca es verdaderamente cero en cadenas profundas.
  • Retardo máximo. Un retardo mayor que 2147483647 (2^31 − 1) desborda el campo de 32 bits y se trata como 0, disparándose casi inmediatamente en lugar de en el futuro lejano.
  • Vinculación de this. Cuando pasas un método como setTimeout(obj.method, 1000), pierde su this. Usa una función flecha — setTimeout(() => obj.method(), 1000) — o obj.method.bind(obj).
  • Limpia siempre. Cancela los intervalos (y los timeouts pendientes) cuando un componente se desmonta o el trabajo ya no es necesario, o tendrás fugas de temporizadores y podrías operar con estado obsoleto.

Temas relacionados

Conclusión

setTimeout() ejecuta código una vez después de un retardo; setInterval() lo ejecuta en un horario repetitivo; clearTimeout() y clearInterval() los cancelan. Recuerda que los retardos son mínimos, pasa los argumentos después del retardo en lugar de dentro de un envoltorio, recurre al patrón setTimeout recursivo cuando necesites espaciado constante, y siempre limpia los temporizadores que ya no necesites. Con debouncing y throttling además, estas dos pequeñas funciones cubren la mayor parte del trabajo basado en tiempo que harás en el navegador.

Práctica

Práctica
¿Cuáles de las siguientes afirmaciones son verdaderas respecto al uso de `setTimeout()` y `setInterval()` en JavaScript?
¿Cuáles de las siguientes afirmaciones son verdaderas respecto al uso de `setTimeout()` y `setInterval()` en JavaScript?
Was this page helpful?