setTimeout()

El método global setTimeout() establece un temporizador que ejecuta una función o una pieza de código específica una vez que expira el temporizador.

Sintaxis

js
setTimeout(code)
setTimeout(code, delay)

setTimeout(functionRef)
setTimeout(functionRef, delay)
setTimeout(functionRef, delay, param1)
setTimeout(functionRef, delay, param1, param2)
setTimeout(functionRef, delay, param1, param2, /* … ,*/ paramN)

Parámetros

functionRef

Una función que se ejecutará después de que expire el temporizador.

code

Una sintaxis alternativa que le permite incluir una cadena en lugar de una función, que se compila y ejecuta cuando expira el temporizador. Esta sintaxis no se recomienda por las mismas razones que hacen que el uso de eval() sea un riesgo para la seguridad.

delay Opcional

El tiempo, en milisegundos, que el temporizador debe esperar antes de que se ejecute la función o el código especificado. Si se omite este parámetro, se utiliza un valor de 0, lo que significa ejecutar "inmediatamente", o más exactamente, el siguiente ciclo de eventos.

Tenga en cuenta que, en cualquier caso, la demora real puede ser mayor de lo previsto; consulte Motivos de los retrasos superiores a los especificados a continuación.

También tenga en cuenta que si el valor no es un número, se realiza una coerción implícita silenciosamente en el valor para convertirlo en un número, lo que puede generar resultados inesperados y sorprendentes; consulte Los valores de retardo no numéricos se transforman silenciosamente en números para ver un ejemplo.

param1, …, paramN Opcional

Argumentos adicionales que se pasan a través de la función especificada por functionRef.

Valor devuelto

El timeoutID devuelto es un valor entero positivo que identifica el temporizador creado por la llamada a setTimeout(). Este valor se puede pasar a clearTimeout() para cancelar el tiempo de espera.

Se garantiza que un valor de timeoutID nunca será reutilizado por una llamada posterior a setTimeout() o setInterval() en el mismo objeto (una ventana o un worker). Sin embargo, los diferentes objetos usan grupos separados de ID.

Descripción

Los tiempos de espera se cancelan usando clearTimeout().

Para llamar a una función repetidamente (por ejemplo, cada N milisegundos), considere usar setInterval().

Los valores de retardo no numéricos se convierten silenciosamente en números

Si se llama a setTimeout() con un valor de delay que no es un número, una coerción implícita se realiza silenciosamente en el valor para convertirlo en un número. Por ejemplo, el siguiente código usa incorrectamente la cadena "1000" para el valor delay, en lugar del número 1000, pero aún así funciona, porque cuando se ejecuta el código, la cadena se convierte en el número 1000. y así el código se ejecuta 1 segundo después.

js
setTimeout(() => {
  console.log("Retrasado por 1 segundo.");
}, "1000");

Pero en muchos casos, la coerción de tipo implícito puede conducir a resultados inesperados y sorprendentes. Por ejemplo, cuando se ejecuta el siguiente código, la cadena "1 segundo" finalmente se convierte en el número 0 y, por lo tanto, el código se ejecuta inmediatamente, sin demora.

js
setTimeout(() => {
  console.log("Retrasado por 1 segundo.");
}, "1 segundo");

Por lo tanto, no use cadenas para el valor de delay, sino que use siempre números:

js
setTimeout(() => {
  console.log("Retrasado por 1 segundo.");
}, 1000);

Trabajando con funciones asíncronas

setTimeout() es una función asíncrona, lo que significa que la función del temporizador no pausará la ejecución de otras funciones en la pila de funciones. En otras palabras, no puede usar setTimeout() para crear una "pausa" antes de que se active la siguiente función en la pila de funciones.

Vea el siguiente ejemplo:

js
setTimeout(() => {
  console.log("este es el primer mensaje");
}, 5000);
setTimeout(() => {
  console.log("este es el segundo mensaje");
}, 3000);
setTimeout(() => {
  console.log("este es el tercer mensaje");
}, 1000);

// Salida:

// este es el tercer mensaje
// este es el segundo mensaje
// este es el primer mensaje

Tenga en cuenta que la primera función no crea una "pausa" de 5 segundos antes de llamar a la segunda función. En su lugar, se llama a la primera función, pero espera 5 segundos para ejecutarse. Mientras la primera función está esperando para ejecutarse, se llama a la segunda función y se aplica una espera de 3 segundos a la segunda función antes de que se ejecute. Dado que ni los temporizadores de la primera ni la segunda función se han completado, se llama a la tercera función y completa su ejecución primero. Luego sigue el segundo. Luego, finalmente, la primera función se ejecuta después de que finalmente se completa su temporizador.

Para crear una progresión en la que una función solo se dispara después de la finalización de otra función, consulte la documentación de Promesas.

El problema de "this"

Cuando pasa un método a setTimeout(), se invocará con un valor this que puede diferir de sus expectativas. El tema general se explica detalladamente en la referencia de JavaScript.

El código ejecutado por setTimeout() se llama desde un contexto de ejecución separado de la función desde la que se llamó a setTimeout. Se aplican las reglas habituales para configurar la palabra clave this para la función llamada, y si no ha configurado this en la llamada o con bind, se establecerá por defecto en el objeto window (o global). No será lo mismo que el valor this para la función que llamó a setTimeout.

Vea el siguiente ejemplo:

js
const myArray = ["cero", "uno", "dos"];
myArray.myMethod = function (sProperty) {
  console.log(arguments.length > 0 ? this[sProperty] : this);
};

myArray.myMethod(); // muestra "cero,uno,dos"
myArray.myMethod(1); // muestra "uno"

Lo anterior funciona porque cuando se llama a myMethod, this se establece en myArray mediante la llamada, por lo que dentro de la función, this[sProperty] es equivalente a myArray[sProperty]. Sin embargo, en lo siguiente:

js
setTimeout(myArray.myMethod, 1.0 * 1000); // muestra "[object Window]" después de 1 segundo
setTimeout(myArray.myMethod, 1.5 * 1000, "1"); // muestra "undefined" después de 1.5 segundos

La función myArray.myMethod se pasa a setTimeout, luego, cuando se llama, this no está configurado, por lo que se establece de manera predeterminada en el objeto window.

Tampoco existe la opción de pasar un thisArg a setTimeout como ocurre en los métodos Array como forEach() y reduce(). Como se muestra a continuación, usar call para configurar this tampoco funciona.

js
setTimeout.call(myArray, myArray.myMethod, 2.0 * 1000); // error
setTimeout.call(myArray, myArray.myMethod, 2.5 * 1000, 2); // mismo error

Soluciones

Usar una función contenedora

Una forma común de resolver el problema es usar una función contenedora que establece this en el valor requerido:

js
setTimeout(function () {
  myArray.myMethod();
}, 2.0 * 1000); // muestra "cero,uno,dos" después de 2 segundos
setTimeout(function () {
  myArray.myMethod("1");
}, 2.5 * 1000); // muestra "uno" después de 2.5 segundos

La función contenedora puede ser una función de flecha:

js
setTimeout(() => {
  myArray.myMethod();
}, 2.0 * 1000); // muestra "cero,uno,dos" después de 2 segundos
setTimeout(() => {
  myArray.myMethod("1");
}, 2.5 * 1000); // muestra "uno" después de 2.5 segundos
Usar bind()

Alternativamente, puede usar bind() para establecer el valor de this para todas las llamadas a una función determinada:

js
const myArray = ["cero", "uno", "dos"];
const myBoundMethod = function (sProperty) {
  console.log(arguments.length > 0 ? this[sProperty] : this);
}.bind(myArray);

myBoundMethod(); // muestra "cero,uno,dos" ya que 'this' está vinculado a myArray en la función
myBoundMethod(1); // muestra "uno"
setTimeout(myBoundMethod, 1.0 * 1000); // sigue mostrando "cero,uno,dos" después de 1 segundo debido a que está vinculado
setTimeout(myBoundMethod, 1.5 * 1000, "1"); // muestra "uno" después de 1.5 segundos

Pasar cadenas

Pasar una cadena en lugar de una función a setTimeout() tiene los mismos problemas que usar eval().

js
// No hacer esto
setTimeout("console.log('¡Hola mundo!');", 500);
js
// Hacer esto en su lugar
setTimeout(() => {
  console.log("¡Hola mundo!");
}, 500);

Una cadena pasada a setTimeout() se evalúa en el contexto global, por lo que los símbolos locales en el contexto donde se llamó a setTimeout() no estarán disponibles cuando la cadena se evalúa como código.

Motivos de los retrasos superiores a los especificados

Hay una serie de razones por las que un tiempo de espera puede tardar más de lo previsto en activarse. Esta sección describe las razones más comunes.

Tiempos de espera anidados

Como se especifica en el estándar HTML, los navegadores impondrán un tiempo de espera mínimo de 4 milisegundos una vez que una llamada anidada a setTimeout se ha programado 5 veces.

Esto se puede ver en el siguiente ejemplo, en el que anidamos una llamada a setTimeout con un retraso de 0 milisegundos, y registramos el retraso cada vez que se llama al manejador. Las primeras cuatro veces, el retraso es de aproximadamente 0 milisegundos, y después de eso, es de aproximadamente 4 milisegundos:

html
<button id="run">Ejecutar</button>
<table>
  <thead>
    <tr>
      <th>Anterior</th>
      <th>Éste</th>
      <th>Retraso actual</th>
    </tr>
  </thead>
  <tbody id="log"></tbody>
</table>
js
let last = 0;
let iterations = 10;

function timeout() {
  // registra la hora de esta llamada
  logline(new Date().getMilliseconds());
  // si no ha terminado, programa la próxima llamada
  if (iterations-- > 0) {
    setTimeout(timeout, 0);
  }
}

function run() {
  // borrar el registro
  const log = document.querySelector("#log");
  while (log.lastElementChild) {
    log.removeChild(log.lastElementChild);
  }

  // inicializar el recuento de iteraciones y la marca de tiempo de inicio
  iterations = 10;
  last = new Date().getMilliseconds();
  // temporizador de inicio
  setTimeout(timeout, 0);
}

function logline(now) {
  // registrar la última marca de tiempo, la nueva marca de tiempo y la diferencia
  const tableBody = document.getElementById("log");
  const logRow = tableBody.insertRow();
  logRow.insertCell().textContent = last;
  logRow.insertCell().textContent = now;
  logRow.insertCell().textContent = now - last;
  last = now;
}

document.querySelector("#run").addEventListener("click", run);

Tiempos de espera en pestañas inactivas

Para reducir la carga (y el uso de batería asociado) de las pestañas en segundo plano, los navegadores impondrán un tiempo de espera mínimo en las pestañas inactivas. También se puede renunciar si una página está reproduciendo sonido usando una API de audio web AudioContext (en-US).

Los detalles de esto dependen del navegador:

  • Firefox Desktop y Chrome tienen un tiempo de espera mínimo de 1 segundo para pestañas inactivas.
  • Firefox para Android tiene un tiempo de espera mínimo de 15 minutos para las pestañas inactivas y puede limpiarla por completo.
  • Firefox no acelera las pestañas inactivas si la pestaña contiene un AudioContext (en-US).

Limitación de scripts de seguimiento

Firefox impone una limitación adicional para las secuencias de comandos que reconoce como secuencias de comandos de seguimiento. Cuando se ejecuta en primer plano, el retraso mínimo de limitación sigue siendo de 4 ms. Sin embargo, en las pestañas en segundo plano, el retraso mínimo de limitación es de 10 000 ms, o 10 segundos, que entra en vigor 30 segundos después de que se haya cargado un documento por primera vez.

Consulte Protección de rastreo para obtener más detalles.

Tiempos de espera tardíos

El tiempo de espera también puede activarse más tarde de lo esperado si la página (o el sistema operativo/navegador) está ocupado con otras tareas. Un caso importante a tener en cuenta es que la función o el fragmento de código no se puede ejecutar hasta que el hilo que llamó setTimeout() haya terminado. Por ejemplo:

js
function foo() {
  console.log("foo ha sido llamado");
}
setTimeout(foo, 0);
console.log("Después de setTimeout");

Escribirá en la consola:

Después de setTimeout
foo ha sido llamado

Esto se debe a que, aunque se llamó a setTimeout con un retraso de cero, se coloca en una cola y se programa para ejecutarse en la próxima oportunidad; no inmediatamente. El código que se está ejecutando actualmente debe completarse antes de que se ejecuten las funciones en la cola, por lo que el orden de ejecución resultante puede no ser el esperado.

Aplazamiento de tiempos de espera durante la carga de la página

Firefox aplazará la activación de los temporizadores setTimeout() mientras se carga la pestaña actual. La activación se difiere hasta que el subproceso principal se considera inactivo (similar a window.requestIdleCallback()), o hasta que se activa el evento de carga.

Temporizadores y páginas de WebExtension

En WebExtensions, setTimeout() no funciona de forma fiable. Los autores de extensiones deben usar la API alarms (en-US) en su lugar.

Valor de retardo máximo

Los navegadores almacenan internamente el retraso como un entero con signo de 32 bits. Esto provoca un desbordamiento de enteros cuando se utilizan retrasos superiores a 2 147 483 647 ms (alrededor de 24,8 días), lo que hace que el tiempo de espera se ejecute inmediatamente.

Ejemplo

Configuración y borrado de tiempos de espera

El siguiente ejemplo configura dos botones simples en una página web y los vincula a las rutinas setTimeout() y clearTimeout(). Al presionar el primer botón, se establecerá un tiempo de espera que muestra un mensaje después de dos segundos y almacena la identificación del tiempo de espera para que la use clearTimeout(). Opcionalmente, puede cancelar este tiempo de espera presionando el segundo botón.

HTML

html
<button onclick="delayedMessage();">
  Mostrar un mensaje después de dos segundos
</button>
<button onclick="clearMessage();">Cancelar mensaje antes de que suceda</button>

<div id="output"></div>

JavaScript

js
let timeoutID;

function setOutput(outputContent) {
  document.querySelector("#output").textContent = outputContent;
}

function delayedMessage() {
  setOutput("");
  timeoutID = setTimeout(setOutput, 2 * 1000, "¡Eso fue muy lento!");
}

function clearMessage() {
  clearTimeout(timeoutID);
}

Resultado

Ver también el ejemplo clearTimeout().

Especificaciones

Specification
HTML Standard
# dom-settimeout-dev

Compatibilidad con navegadores

BCD tables only load in the browser

Véase también