Aprende leyendo en orden

Temporizadores y fechas — setTimeout / Date / Intl

setTimeout y setInterval y cómo detenerlos, los meses de Date que empiezan en 0, Intl.DateTimeFormat y los días entre dos fechas.

Si el código solo avanza de arriba abajo, no puedes cerrar un aviso a los 3 segundos ni descontar cada segundo el tiempo que queda. Y mientras una fecha sea solo un texto, tampoco puedes mostrar qué día de la semana cae ni contar los días que faltan para una fecha límite.

En este artículo verás setTimeout y setInterval, que crean temporizadores, y Date e Intl.DateTimeFormat, que trabajan con fechas y horas.

Programar una llamada para después — setTimeout y clearTimeout

Imagina que un sitio para compartir fotos muestra el aviso «Subida completada» y quieres cerrarlo a los 3 segundos. El código que avanza de arriba abajo no tiene forma de decir «espera 3 segundos y luego sigue», y si haces tiempo con un for vacío, la página deja de responder a los clics y de actualizarse mientras dura el bucle.

setTimeout(función, milisegundos) solo programa la llamada y la ejecución pasa de inmediato a la línea siguiente. Devuelve un ID de temporizador, igual que el que usaste para el tiempo de espera de fetch, y si pasas ese ID a clearTimeout, la llamada se cancela.

// Devuelve una Promise que pasa a fulfilled pasados ms milisegundos (el delay del artículo de Promise, sin value)
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

// Programa el cierre del aviso en 100ms y sigue enseguida (en una página real, unos 3000ms)
setTimeout(() => console.log("Aviso cerrado"), 100);          // Aviso cerrado (2.º)
console.log("Subida completada");                             // Subida completada (1.º)

// El usuario salió de la página, así que se cancelan las recomendaciones. No se llama ni a los 100ms
const recommendTimer = setTimeout(() => console.log("Mostrando fotos recomendadas"), 100);
clearTimeout(recommendTimer);

// No pasa de esta línea hasta que transcurren 200ms
await delay(200);
console.log("Pasaron 200ms");                                 // Pasaron 200ms (3.º)
El orden en que escribes no es el orden en que se muestra
0msSubida completadaSe muestra 1.ºsetTimeout nomuestra nada0msRecomendacionescanceladasNo se llamani a los 100ms100msAviso cerradoSe muestra 2.º,durante el await200msPasaron 200msTras el await,se muestra 3.º
La línea de setTimeout no muestra nada, y la llamada cancelada nunca se ejecuta. La función programada se ejecuta a los 100ms, mientras await está esperando.

Una función programada puede ejecutarse cuando tu código ya llegó a la última línea. Para ver su salida en la consola de los ejercicios, termina el código con una línea que espere, como await delay(…), igual que en el artículo de Promise.

En una app de correo, retrasa el envío para que todavía se pueda cancelar. delay ya está declarado.

① Programa que se muestre «Correo 1 enviado» en 150ms, espera 50ms y cancélalo.

② Muestra «Se canceló el envío del correo 1».

③ Programa que se muestre «Correo 2 enviado» en 100ms, espera 150ms y cancélalo.

④ Muestra «No se pudo cancelar el correo 2».

(Si se ejecuta correctamente, aparecerá una explicación.)

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Detener una repetición — setInterval y clearInterval

Ahora quieres mostrar los segundos que faltan para que termine una oferta, descontando uno cada segundo. setTimeout llama a la función programada una sola vez, así que para repetir, la función tendría que volver a programarse en cada llamada, y la condición para detenerse quedaría mezclada en ese mismo código.

setInterval (una función que llama a otra función una y otra vez, cada cierto número de milisegundos) sigue llamándola hasta que pasas a clearInterval el ID de temporizador que devolvió.

Para ejecutar la línea siguiente solo cuando termine la repetición, envuelve todo en una Promise y llama a resolve justo después de clearInterval, dentro del mismo if.

// Descuenta 1 segundo del tiempo restante cada 100ms (cada 1000ms en una página real)
function startCountdown(seconds) {
  return new Promise((resolve) => {
    let remaining = seconds;                  // Fuera de la función que se llama, para conservar el valor entre llamadas
    const timerId = setInterval(() => {
      remaining -= 1;
      console.log(`Quedan ${remaining} s`);    // Quedan 2 s → Quedan 1 s → Quedan 0 s
      if (remaining === 0) {
        clearInterval(timerId);               // Se detiene en la llamada que llega a 0 (para entonces timerId ya tiene su valor)
        resolve();                            // Avisa a la línea del await de que terminó
      }
    }, 100);
  });
}

await startCountdown(3);
console.log("La oferta terminó");             // La oferta terminó
resolve hace avanzar al await
Quien llama — la línea await startCountdown(3)
  • No pasa a la línea siguiente hasta que la Promise devuelta pasa a fulfilled
  • La oferta terminó se muestra después de resolve
Dentro de startCountdown(3) — se ejecuta 1 vez
  • let remaining = seconds — se crea una sola vez aquí, con el valor 3
  • const timerId = setInterval(...) — el ID que pasas para detenerlo
Función llamada cada 100ms — se ejecuta 3 veces
  • remaining -= 1 — resta 1 a la variable externa: 2 → 1 → 0
  • En la llamada que llega a 0, clearInterval(timerId) y resolve()
La variable externa remaining se crea una sola vez y baja 1 en cada llamada. Llamar a resolve en la llamada que llega a 0 hace avanzar el await de quien llama.

Si mueves let remaining = seconds dentro de la función que se llama, vuelve a empezar en 3 en cada llamada y nunca baja de 2, así que no se llega a llamar ni a clearInterval ni a resolve. Pasados 15 segundos sin avanzar, la consola de los ejercicios indica que la ejecución superó el tiempo límite.

Una repetición que no detienes se cuela en las siguientes ejecuciones

La consola de los ejercicios usa la misma página para todas las ejecuciones de este artículo. Si una ejecución termina sin llamar a clearInterval, esa función se sigue llamando hasta que recargas la página, y su salida se mezcla con el resultado de los ejercicios que ejecutes después y que esperen con await, así que incluso el código correcto se marca como incorrecto. Si te pasa, recarga la página del artículo.

Lee el inventario de un almacén cada 100ms y detente tras 3 lecturas. El esqueleto de watchStock, el array de lecturas readings y delay ya están declarados.

① Cada 100ms, lee el siguiente valor de readings y muéstralo en la forma «Lectura 1: 12 unidades».

② Detente tras la 3.ª lectura y haz que la Promise pase a fulfilled.

③ Espera a que termine y luego espera 200ms más para comprobar que no aparece una 4.ª lectura.

④ Muestra «Revisión de inventario terminada».

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Convertir una fecha en texto para mostrar — getMonth e Intl

Supón que una lista de videos muestra la fecha de publicación en la forma «mié, 2 de septiembre de 2026». Si sacas el mes con slice de un texto de la API como "2026-09-02T09:00:00", obtienes la cadena "09", y como el día de la semana no aparece en ninguna parte, no puedes deducirlo solo a partir del texto.

Date (un objeto integrado que representa una fecha y hora) lee "2026-09-02T09:00:00" como las 9:00 a.m. en la zona horaria del dispositivo (la hora local de una región, definida por su diferencia con la hora de referencia UTC), y sus métodos que empiezan por get devuelven el mes, el día de la semana, etc., como números.

Intl.DateTimeFormat (una clase que convierte fechas y horas en texto según el idioma) se crea pasándole una configuración regional como "es-MX" (español de México).

const publishedAt = new Date("2026-09-02T09:00:00");

// Los métodos que empiezan por get devuelven números. En los meses, enero es 0; en los días de la semana, domingo es 0
console.log(publishedAt.getMonth(), publishedAt.getDate(), publishedAt.getDay());   // 8 2 3

// Crea un formateador pasando el idioma y el estilo de cada parte que quieres mostrar
const dateFormat = new Intl.DateTimeFormat("es-MX", {
  year: "numeric",     // Año como número
  month: "long",       // Mes con su nombre, como "septiembre"
  day: "numeric",      // Día como número
  weekday: "short",    // Día de la semana con su nombre corto
});
console.log(dateFormat.format(publishedAt));   // mié, 2 de septiembre de 2026
console.log(new Intl.DateTimeFormat("es-MX", { hour: "numeric", minute: "2-digit" }).format(publishedAt));   // 9:00 a.m.

// Un texto sin hora se lee como las 0:00 UTC
const dateOnly = new Date("2026-09-02");
console.log(dateOnly.getDate());   // 2 en Japón, 1 en Nueva York (8:00 p.m. del día anterior)
Sin la hora, obtienes las 0:00 UTC
"2026-09-02T09:00:00"Se lee como 9:00a.m. hora localTambién enNueva York:2 sep, 9:00 a.m.getDate() es 2igual que en Tokio"2026-09-02"sin la horaSe lee como0:00 UTCEn Nueva York:1 sep, 8:00 p.m.getDate() es 1el día anterior
Con la hora incluida, el texto significa las 9:00 a.m. del 2 de septiembre en la zona horaria que use el dispositivo. Sin la hora, se lee como UTC y, según la diferencia horaria, puede caer en el día anterior.

En zonas horarias por delante de UTC, como Japón, sigues obteniendo 2, así que el problema pasa desapercibido con facilidad. Cuando la API envía un texto solo con la fecha, agrégale "T00:00:00" antes de pasarlo a new Date para que se lea como las 0:00 en hora local. La tabla siguiente resume lo que devuelven los métodos get para publishedAt.

MétodoEn publishedAtPara mostrarlo
getFullYear()2026Úsalo tal cual
getMonth()8Enero es 0: suma 1 y da 9
getDate()2Empieza en 1: úsalo tal cual
getDay()3Domingo es 0: 3 es miércoles
getHours()9Formato de 24 h, 0–23

En la página de citas de un consultorio dental, convierte la fecha y hora de la cita en texto para mostrar. La fecha y hora de la cita, reservedAt, ya está declarada.

① Con los métodos que empiezan por get, muestra la fecha de la cita en la forma «5/10» (día/mes).

② Crea un formateador en español de México que muestre el día de la semana, el día y el mes en la forma «lun, 5 de octubre».

③ Muestra la fecha de la cita con el formateador de ②.

④ Crea un formateador que muestre la hora y los minutos en la forma «2:30 p.m.» y muestra la hora de la cita.

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Contar los días hasta una fecha límite — restar fechas

El sitio de una biblioteca muestra cuántos días faltan para devolver un libro. Si restas solo los números del día, un libro prestado el 2 de septiembre que vence el 3 de octubre da 3 - 2 = 1 día, porque no se cuentan los días de septiembre que hay en medio.

Por dentro, un Date es un número: los milisegundos transcurridos desde las 0:00 UTC del 1 de enero de 1970. Restar un Date a otro da la diferencia en milisegundos, así que al dividirla entre los milisegundos de un día, 24 * 60 * 60 * 1000, obtienes el número de días.

new Date(año, mes, día) crea las 0:00 en hora local de ese día, y aquí también los meses se cuentan desde 0.

// Milisegundos de un día (24 horas × 60 minutos × 60 segundos × 1000)
const DAY_MS = 24 * 60 * 60 * 1000;

// Fecha de préstamo y fecha límite. Al crearlas con números, los meses también se cuentan desde 0
const borrowedAt = new Date(2026, 8, 2);     // 2 de septiembre de 2026
const dueDate = new Date(2026, 9, 3);        // 3 de octubre de 2026

// Restar un Date a otro da la diferencia en milisegundos
const diffMs = dueDate - borrowedAt;
console.log(Math.round(diffMs / DAY_MS));    // 31

// Si pasas 31, un día que septiembre no tiene, no hay error: pasa al mes siguiente
const overflowDate = new Date(2026, 8, 31);
console.log(overflowDate.getMonth() + 1, overflowDate.getDate());   // 10 1
Tres formas de escribir el 30 de septiembre
Días desdenew Date(2026,8,2)al vencimientonew Date(2026, 8, 30)new Date(2026, 9, 30)new Date(2026, 8, 31)Se crea el30 de septiembre30 de octubremes 9 = octubre1 de octubreno hay 31 de sept.28 días58 días30 días de más29 días1 día de más
Las tres pretenden fijar la fecha límite el 30 de septiembre. Pasar 9 como mes o 31 como día no produce ningún error; solo obtienes un número de días equivocado.

Se redondea con Math.round por el horario de verano (adelantar los relojes una hora durante el verano). Donde se aplica, algunos días duran 23 o 25 horas, así que la división no siempre da un número entero (puede salir algo como 30.958…). En lugares sin horario de verano, como Japón, la división siempre es exacta, así que es fácil olvidar el redondeo.

En el sitio de reservas de un campamento, calcula el número de noches de una estancia. DAY_MS y los esqueletos de toDate y countNights ya están declarados.

① En toDate, recibe el año, el mes (contando desde 1) y el día, y devuelve un Date.

② En countNights, devuelve el número de noches a partir de la diferencia entre dos Date.

③ Muestra el número de noches del 28 de septiembre al 2 de octubre de 2026 en la forma «4 noches».

④ Muestra con la misma forma el número de noches del 29 de diciembre de 2026 al 3 de enero del año siguiente.

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado
QUIZ

Verificación de conocimientos

Responde cada pregunta una a una.

Pregunta 1¿Qué pasa si declaras let count = 0; dentro de la función que pasas a setInterval e intentas detenerla cuando count llegue a 3?

Pregunta 2¿Qué devuelve new Date("2026-09-02T09:00:00").getMonth()?

Pregunta 3¿Qué devuelve getDate() en un Date creado con new Date(2026, 8, 31)?