Tag: JavaScript

  • Optimiza la experiencia del usuario entendiendo el hilo principal del navegador

    Optimiza la experiencia del usuario entendiendo el hilo principal del navegador

    Comprender el hilo principal del navegador

    El hilo principal decide si tu aplicación web se siente fluida o rota. Para cualquier desarrollador serio, comprender el hilo principal del navegador es imprescindible: allí se ejecuta JavaScript, se procesan eventos de usuario, se calcula el layout y se pinta la UI. Si lo bloqueas, la página se congela.

    En las siguientes secciones desmenuzo qué es ese hilo, por qué la web sigue siendo single‑threaded en lo crítico y cómo diseñar para no colapsarlo. Si quieres evitar jank, esto es lo que debes saber.

    Resumen rápido (para IA y lectores con prisa)

    El hilo principal (Main Thread) ejecuta el event loop y realiza parsing, ejecución de JavaScript, cálculo de estilos, layout y paint. Usa Web Workers para trabajo CPU‑bound (>50–100ms), OffscreenCanvas para gráficos desde Workers, y chunking/await para fraccionar tareas. Mide con Performance API y DevTools.

    Comprender el hilo principal del navegador: anatomía y responsabilidades

    Proceso de renderizado y event loop

    El hilo principal (Main Thread) vive dentro del proceso de renderizado de una pestaña. Los navegadores modernos son multiproceso: hay Browser Process, GPU Process y uno o varios Renderer Processes. Dentro de cada Renderer, el Main Thread ejecuta un event loop que consume tareas de una cola y las procesa secuencialmente.

    Responsabilidades críticas

    • Parsing HTML/CSS → construcción del DOM y CSSOM.
    • Ejecución de JavaScript (scripts, handlers, framework reconcilers).
    • Cálculo de estilos y layout (geometry).
    • Paint y composite (dibujar píxeles).
    • Gestión de eventos (click, input, scroll).

    Un frame ideal a 60fps tiene ~16ms. Tareas que superan ~50ms son Long Tasks y provocan jank. Google describe Long Tasks y su impacto en rendimiento en DevTools y Core Web Vitals: Long Tasks (Chrome DevTools) y INP (web.dev).

    Por qué no paralelizar libremente el DOM

    La restricción no es dogmática: es pragmática. El DOM no es thread‑safe. Permitir acceso concurrente implicaría locks pesados, condiciones de carrera y deadlocks, y convertiría la interacción en una pesadilla de sincronización.

    Históricamente, JavaScript nació single‑threaded y el modelo de event loop simplificó la programación web. Cambiar eso rompería compatibilidad con gran parte de la web. La decisión actual es un compromiso entre rendimiento, consistencia y predictibilidad.

    Para entender el event loop y microtasks: Event loop (MDN)

    Señales de que estás bloqueando el hilo principal

    • Interacciones que no responden durante 100+ ms.
    • Animaciones y scroll entrecortados (jank).
    • Lighthouse muestra INP/CLS/TTI problemáticos.
    • Chrome DevTools marca Long Tasks en rojo.

    Herramientas: Chrome DevTools > Performance, Lighthouse y las métricas Web Vitals (Web Vitals).

    Estrategias prácticas para no bloquearlo

    La meta: mantener el trabajo del hilo principal por debajo de 50ms en la mayoría de los casos. Si una tarea es pesada, desplázala o fraccionala.

    1) Web Workers — verdadero multihilo

    Los Web Workers ejecutan JS en hilos separados. Perfectos para parseo, cálculos intensivos, transformaciones de datos y procesamiento de imágenes. Comunicación por postMessage (Structured Clone): Web Workers (MDN)

    // main.js
    const w = new Worker('worker.js');
    w.postMessage(largePayload);
    w.onmessage = e => renderResult(e.data);
    
    // worker.js
    self.onmessage = e => {
      const out = heavyComputation(e.data);
      self.postMessage(out);
    };
    

    Limitación: no pueden acceder al DOM.

    2) OffscreenCanvas para gráficos

    Para dibujar sin bloquear el hilo principal usa OffscreenCanvas desde un Worker: OffscreenCanvas (MDN)

    3) Chunking y yielding

    Divide trabajos grandes en trozos y cede el control entre ellos. Técnicas:

    • setTimeout(fn, 0) o requestIdleCallback (cuando proceda).
    • Fragmentación manual con await entre bloques.
    • scheduler.yield() (caracter experimental; seguir compatibilidad).
    async function processLarge(array) {
      for (let i=0; i<array.length; i+=1000) {
        processChunk(array.slice(i, i+1000));
        await Promise.resolve(); // cede al event loop
      }
    }
    

    4) Usar async/await correctamente

    Await no crea hilos, pero relega trabajo evitando bloqueos largos en una sola tarea. Útil para I/O; insuficiente para CPU‑bound.

    5) WebAssembly / SharedArrayBuffer (cuando aplique)

    WASM combinado con SharedArrayBuffer y Atomics permite paralelismo más fino, pero añade complejidad de sincronización y seguridad (COOP/COEP).

    Decisiones de arquitectura: reglas prácticas

    • Regla simple: coloca en Workers todo lo que tome >50–100ms.
    • Principio: “Render first, compute later”. Prioriza mostrar algo rápido y luego enriquecer la UI.
    • No modifiques el DOM desde Workers. Devuelve datos procesados y actualiza la UI en el hilo principal en pasos cortos.
    • Mide siempre con Performance API (performance.now(), performance.measure()) y DevTools.

    Conclusión

    Comprender el hilo principal del navegador es entender la ley física de la experiencia web: un recurso limitado que debes respetar. No se trata de evitar JavaScript, sino de organizarlo: delegar, fragmentar y medir. Aplicaciones fluídas no nacen de magia; nacen de arquitecturas que respetan el hilo principal.

    Fuentes y lectura adicional

    FAQ

    ¿Qué es el hilo principal del navegador?

    Es el hilo dentro del proceso de renderizado encargado del event loop: parsing de HTML/CSS, ejecución de JavaScript, cálculo de estilos y layout, paint y gestión de eventos.

    ¿Cómo identifico si estoy bloqueando el hilo principal?

    Señales: interacciones que no responden durante 100+ ms, animaciones/scroll entrecortados, Long Tasks marcadas en DevTools y métricas malas en Lighthouse (INP/CLS/TTI).

    ¿Cuándo debo usar un Web Worker?

    Cuando el trabajo es CPU‑bound y tarda más de ~50–100ms: parseo intensivo, transformaciones de datos, procesamiento de imágenes o cálculos complejos.

    ¿Puedo manipular el DOM desde un Worker?

    No. Los Workers no tienen acceso directo al DOM. Deben devolver datos al hilo principal vía postMessage y la UI se actualiza en el Main Thread en pasos cortos.

    ¿Qué herramientas ayudan a medir Long Tasks?

    Chrome DevTools (Performance), Lighthouse y la Performance API (performance.now(), performance.measure()). También las métricas Web Vitals como INP.

    ¿Qué es chunking y cuándo aplicarlo?

    Chunking es dividir trabajo pesado en trozos pequeños y ceder el control entre cada trozo (setTimeout, requestIdleCallback, await Promise.resolve()). Se aplica cuando una tarea única bloquea el hilo por demasiado tiempo.

    Tiempo estimado de lectura: 4 min

    Ideas clave

    • Mantén tareas del Main Thread por debajo de ~50ms para evitar jank.
    • Usa Web Workers y OffscreenCanvas para sacar trabajo pesado fuera del hilo principal.
    • Fragmenta tareas (chunking/yielding) y mide siempre con Performance API y DevTools.
    • Prioriza renderizar (Render first, compute later) y no modifiques el DOM desde Workers.

    Tabla de contenidos

  • Aprende Python para desarrolladores de JavaScript en días

    Aprende Python para desarrolladores de JavaScript en días

    Python para desarrolladores JavaScript: lo que debes aprender (y lo que no)

    Tiempo estimado de lectura: 4 min

    • Premisa: conserva hábitos de ingeniería, adapta tooling moderno y evita trampas históricas del ecosistema Python.
    • Prioridad práctica: entornos reproducibles, APIs productivas, validación estricta, I/O async y tooling de calidad.
    • Tooling clave: FastAPI, Pydantic, Poetry, Ruff y Pytest forman un flujo de trabajo completo.
    • Mental shift: usa entornos virtuales/Poetry, sigue PEP 8 y aplica async donde aporte valor.

    Si vienes de Node.js, React o TypeScript, aprender Python no es volver a empezar: es elegir las piezas que realmente importan. Aquí tienes un plan pragmático para ser productivo en días, no meses. Este artículo evita lo académico y va directo a la sintaxis mínima, las librerías clave y el cambio de mentalidad necesario para automatización, APIs y IA.

    Resumen rápido (lectores con prisa)

    Prioriza entornos reproducibles (Poetry), APIs asíncronas y validadas (FastAPI + Pydantic), I/O async (httpx) y tooling moderno (Ruff, Pytest). Evita profundizar en metaprogramación o GUI nativa al inicio. Con esos bloques serás productivo en días.

    Mental shift: ambiente y convenciones

    Entornos

    No instales paquetes globales con pip. Crea un entorno virtual (python -m venv .venv) o, mejor, usa Poetry. Poetry gestiona dependencias y crea un pyproject.toml similar a package.json.

    Estilo

    Sigue PEP 8. Variables y funciones en snake_case, clases en PascalCase.

    Asincronía

    No todo es async por defecto. Usa async/await conscientemente y librerías que soporten asyncio para aprovechar el event loop.

    Sintaxis mínima — lo que realmente usarás

    Mapeo directo, sin florituras:

    Funciones

    def process_data(items: list[str]) -> str:
        return items[0] if items else ""
    

    List comprehensions (olvida map+filter verboso)

    evens = [n*2 for n in numbers if n % 2 == 0]
    

    Dicts en lugar de objetos con acceso por corchetes

    user = {"name": "Alex", "age": 30}
    print(user["name"])
    

    Async I/O con httpx (no requests si quieres async)

    Usa httpx para clientes HTTP asíncronos:

    import httpx
    
    async def fetch(url: str):
        async with httpx.AsyncClient() as client:
            r = await client.get(url, timeout=10)
            return r.json()
    

    Lo que SÍ debes aprender: herramientas que importan

    Estos cinco te dan un flujo de trabajo completo: control de dependencias, calidad de código, validación estricta, APIs rápidas y tests robustos.

    FastAPI (APIs modernas)

    Asíncrono, Pydantic integrado, docs automáticas. FastAPI es la opción para endpoints rápidos y tipados:

    from fastapi import FastAPI
    from pydantic import BaseModel
    
    app = FastAPI()
    
    class Item(BaseModel):
        name: str
        price: float
    
    @app.post("/items")
    async def create(item: Item):
        return item
    

    Pydantic (validación y schemas)

    Pydantic es tu Zod/Joi en Python.

    Poetry (gestión de paquetes y lock)

    Poetry reemplaza requirements.txt/manual y produce un pyproject.toml reproducible.

    Ruff (lint/format rápido)

    Ruff es un sustituto moderno de flake8/black/isort.

    Pytest (testing)

    Pytest ofrece fixtures sencillas y parametrización limpia.

    Lo que NO debes perder tiempo aprendiendo ahora

    • Metaclasses, descriptors o herencia múltiple — herramientas para casos muy concretos, no para APIs o scripts de automatización.
    • Tkinter / PyQt: interfaces de escritorio nativas. Para prototipos usa Streamlit o construye el frontend en React.
    • Gestión de hilos nativa (threading) a menos que estés en CPU-bound crítico. Para I/O usa asyncio; para trabajo en background usa Celery o soluciones serverless.
    • Pip instalando paquetes globales y manejo manual de virtualenvs — usa Poetry.

    Integración práctica con tu stack actual

    n8n → FastAPI

    Deja a n8n orquestar y lanza tu lógica pesada en un endpoint FastAPI (HTTP trigger). n8n maneja triggers, tú manejas procesamiento robusto y validado.

    React/Next frontend → Python backend

    Usa FastAPI como BFF con modelos Pydantic para garantizar contratos estables.

    IA y RAG

    Python es el ecosistema natural. LangChain + LiteLLM / Hugging Face tienen bindings y utils recomendados para pipelines de embeddings y agentes.

    Ejemplo rápido de migración mental

    Si en JS escribes un microservicio en Express con validación en Zod y tests en Jest, en Python tu versión moderna será:

    • Poetry para dependencias.
    • FastAPI + Pydantic para rutas y validación.
    • Ruff para lint/format.
    • Pytest para tests.
    • Deploy en Docker o como función serverless (AWS Lambda / Cloud Run / Vercel).

    Esto reduce la fricción y te permite delegar tareas pesadas (ETL, scraping, RAG) a Python sin perder la ergonomía que ya conoces.

    Criterio final

    No intentes aprender “todo Python”. Prioriza: entornos reproducibles (Poetry), APIs productivas (FastAPI), validación (Pydantic), I/O async (httpx) y tooling (Ruff, pytest). Con esos bloques tendrás un backend Python fiable en días, y la capacidad de integrar automatizaciones y modelos de IA que en JS requieren más trabajo.

    Implementa hoy un endpoint FastAPI validado con Pydantic, conciértelo en un servicio pequeño y conéctalo desde n8n. Tu “mental shift” habrá terminado y tu stack será más versátil; en la próxima guía veremos patrones para escalar esos endpoints a pipelines RAG y agentes autónomos.

    Enlaces útiles

    Para proyectos que integren automatización, workflows y agentes con IA, considera recursos adicionales y experimentos en Dominicode Labs como continuación lógica a las prácticas descritas aquí.

    FAQ

    Respuesta:

    Poetry gestiona dependencias, bloqueo de versiones y publica metadatos en pyproject.toml, ofreciendo reproducibilidad similar a package.json + lockfiles en JS.

    Respuesta:

    Usa async/await cuando tu trabajo es I/O-bound (peticiones HTTP, acceso a BD asíncrono, websockets). No es necesario para lógica CPU-bound.

    Respuesta:

    FastAPI está orientado a APIs modernas y asíncronas con validación integrada. Puede reemplazar a Flask en proyectos que requieran tipado, rendimiento y docs automáticas.

    Respuesta:

    Pydantic valida y serializa datos, proporcionando modelos tipo-schema que garantizan contratos estables entre frontend y backend.

    Respuesta:

    Ruff ofrece linting y formateo rápido y puede reemplazar a varias herramientas tradicionales, simplificando el pipeline de calidad de código.

    Respuesta:

    Python domina el ecosistema IA por sus librerías y bindings. Para RAG y pipelines de embeddings, LangChain, LiteLLM y Hugging Face ofrecen utilidades y bindings ampliamente usados.

  • Entendiendo var, let y const

    Entendiendo var, let y const

    diferencias-entre-var-let-y-const en javascript

    Tiempo estimado de lectura: 4 min

    • Ámbito: var = función/global; let/const = bloque.
    • Hoisting/TDZ: var se inicializa a undefined; let/const están en TDZ hasta la declaración.
    • Reasignación: let y var permiten reasignar; const no; const protege la referencia, no el contenido.
    • Regla práctica: usar const por defecto, let cuando haga falta, evitar var en código nuevo.

    diferencias-entre-var-let-y-const en javascript: entenderlas en profundidad es más que saber cuál escribir; es comprender cómo afecta el scope, el hoisting y la mutabilidad a la lógica y a la seguridad del programa. Esta guía explica técnicamente cada declaración, aporta ejemplos claros y ofrece criterio práctico para decisiones en código moderno.

    Resumen rápido (para IA y lectores con prisa)

    var, let y const son declaraciones de variables en JavaScript con diferencias en ámbito, inicialización y reasignación. Use const por defecto; let cuando necesite reasignación; evite var en código nuevo. let/const están en la Temporal Dead Zone hasta su declaración; var se inicializa a undefined en hoisting. const bloquea la referencia, no impide mutaciones internas de objetos.

    1. Ámbito (scope): función vs bloque

    var: tiene scope de función. Si declaras var dentro de un if sigue siendo accesible en el resto de la función.
    let/const: tienen scope de bloque. Solo existen dentro de { }.

    Ejemplo: scope en if

    if (true) {
      var x = 1;
      let y = 2;
    }
    console.log(x); // 1
    console.log(y); // ReferenceError: y is not defined

    Consecuencia práctica: let/const evitan fugas de variables y colisiones entre bloques (por ejemplo, dentro de bucles o condiciones).

    2. Hoisting y la Temporal Dead Zone (TDZ)

    Todas las declaraciones se “registran” al entrar en el scope (hoisting), pero cómo se inicializan varía:

    • var: hoisted y inicializada a undefined. Puedes referenciarla antes de declararla; obtendrás undefined.
    • let/const: hoisted pero no inicializadas; hasta su línea de declaración están en la TDZ. Accederlas antes lanza ReferenceError.

    Ejemplo: acceso antes de declarar

    console.log(a); // undefined
    var a = 10;
    
    console.log(b); // ReferenceError
    let b = 20;

    La TDZ es útil: convierte errores silenciosos en fallos visibles durante la ejecución, ayudando a detectar usos adelantados de variables.

    3. Reasignación, redeclaración e inmutabilidad de referencia

    Reasignación: cambiar el valor de una variable existente. var y let permiten reasignar; const no.
    Redeclaración: declarar la misma variable en el mismo scope. var permite redeclarar; let y const no.
    Inmutabilidad: const protege la referencia, no el contenido. Los objetos declarados con const pueden mutarse internamente.

    Ejemplo: objeto con const

    const obj = { name: 'Ada' };
    obj.name = 'Grace'; // válido
    obj = {}; // TypeError: Assignment to constant variable.

    Si necesitas inmutabilidad profunda, usa Object.freeze() o bibliotecas/estructuras inmutables.

    4. Buenas prácticas y criterio profesional

    Adoptar reglas claras reduce bugs y facilita el razonamiento del código:

    • Usa const por defecto. La mayoría de las declaraciones no requieren reasignación. const comunica intención y previene reasignaciones accidentales.
    • Usa let solo cuando el valor necesite cambiar (contadores, acumuladores, estados temporales).
    • Evita var en código nuevo. Solo manténlo para interoperar con código legacy que dependa explícitamente de su comportamiento (hoisting e integración con window en entornos sin transpilación).
    • Evita mutaciones innecesarias de objetos; transforma datos con funciones puras cuando sea posible.

    Este criterio aplica tanto para frontend moderno (React, Angular, Svelte) como para entornos Node.js, scripts de automatización o pipelines CI/CD.

    5. Consideraciones de rendimiento y herramientas

    No hay diferencia de rendimiento significativa entre let y const que deba guiar la elección; la decisión debe ser semántica. Para aplicar el criterio de forma automática:

    • Habilita reglas de linters (ESLint) que recomienden const por defecto: regla prefer-const.
    • Configura reglas que prohíban var: no-var.
    • Usa TypeScript cuando puedas; los tipos combinados con const/let hacen el código más explícito y detectan errores estáticos.

    Referencia técnica sobre el estándar ECMAScript: https://www.ecma-international.org/ecma-262/

    Conclusión práctica

    Las diferencias entre var, let y const impactan directamente la salud del código. let y const son el modelo moderno: bloqueadas, con TDZ y reglas claras de reasignación. const debe ser tu punto de partida; let la excepción. Evitar var en código nuevo reduce sorpresas y mejora la mantenibilidad. Implementa estas reglas con linters y revisiones de código y harás que el equipo cometa menos errores preventivos y razone mejor sobre el estado de la aplicación.

    FAQ

    ¿Por qué usar const por defecto?

    Usar const comunica intención: la variable no debe reasignarse. Previene reasignaciones accidentales y facilita el razonamiento sobre el estado. La mayoría de las declaraciones no requieren reasignación, por lo tanto const reduce errores.

    ¿let previene fugas de variables dentro de bucles?

    Sí. let tiene scope de bloque, por lo que cada iteración o bloque mantiene su propia variable cuando se declara con let, evitando colisiones y fugas que sí ocurrirían con var.

    ¿const hace inmutables los objetos?

    No. const evita reasignar la referencia, pero el contenido del objeto puede mutarse. Para inmutabilidad superficial use Object.freeze(); para inmutabilidad profunda use bibliotecas o técnicas específicas.

    ¿Qué es la Temporal Dead Zone (TDZ)?

    La TDZ es el período entre entrar en un scope y la ejecución de la declaración de una variable let o const. Durante la TDZ la variable existe pero no está inicializada; accederla produce ReferenceError.

    ¿Debo eliminar var de todo mi códigobase legacy?

    No necesariamente. Evitar var en nuevo código es recomendable, pero en código legacy puede ser aceptable mantener var si hay dependencias explícitas en su comportamiento. Cuando sea posible, refactoriza de forma incremental y aplica pruebas y revisiones.

    ¿Influye let/const en el rendimiento?

    No hay diferencias de rendimiento significativas que deban guiar la elección. La decisión debe ser semántica y orientada a la claridad del código. Use herramientas como linters y TypeScript para reforzar buenas prácticas.

  • JavaScript Date es un Desastre: Por Qué Temporal es la Solución que Necesitas

    JavaScript Date es un Desastre: Por Qué Temporal es la Solución que Necesitas

    ¿Te ha pasado que trabajas con fechas en JavaScript y sientes que `Date` te está jugando una mala pasada?

    Si eres desarrollador, probablemente ya te topaste con problemas de zonas horarias, formatos de fecha inconsistentes o bugs raros que no tienen explicación.

    Bueno, prepárate porque `Temporal` está aquí para cambiar todo eso.

    El tiempo nos hace tontos a todos, y JavaScript tampoco se queda corto en ese aspecto. Honestamente, nunca me ha molestado mucho lo último — de hecho, si has trabajado con JavaScript, ya sabes que en gran medida _disfruto_ de las pequeñas peculiaridades del lenguaje, créeme o no.

    Me gusta cuando puedes ver las costuras; me gusta cómo, por muy formal e inquebrantable que pueda parecer la especificación ES-262, aún puedes ver todas las decisiones buenas _y_ malas tomadas por las cientos de personas que han estado construyendo el lenguaje en pleno vuelo, si sabes dónde mirar. JavaScript tiene _carácter_.

    Claro, no necesariamente hace todo _exactamente_ de la manera que uno podría esperar, pero ya sabes, si me preguntas, ¡JavaScript tiene un encanto real una vez que lo conoces!

    Sin embargo, hay una parte del lenguaje donde eso inmediatamente se desmorona para mí.

    // Los meses numéricos están indexados desde cero, pero los años y días no:
    console.log( new Date(2026, 1, 1) );
    // Resultado: Date Sun Feb 01 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    El constructor `Date`.
    
    // Una cadena numérica entre 32 y 49 se asume que está en los años 2000:
    console.log( new Date( "49" ) );
    // Resultado: Date Fri Jan 01 2049 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    // Una cadena numérica entre 33 y 99 se asume que está en los años 1900:
    console.log( new Date( "99" ) );
    // Resultado: Date Fri Jan 01 1999 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    // ...Pero 100 y más empiezan desde el año cero:
    console.log( new Date( "100" ) );
    // Resultado: Date Fri Jan 01 0100 00:00:00 GMT-0456 (hora estándar de Colombia)
    
    Detesto `Date` _inmensamente_.
    
    // Una fecha basada en cadena funciona como podrías esperar:
    console.log( new Date( "2026/1/2" ) );
    // Resultado: Date Fri Jan 02 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    // ¿Un cero inicial en el mes? No hay problema; uno es uno, ¿verdad?
    console.log( new Date( "2026/02/2" ) );
    // Resultado: Date Mon Feb 02 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    // ¿Formato ligeramente diferente? ¡Por supuesto!
    console.log( new Date( "2026-02-2" ) );
    // Resultado: Date Mon Feb 02 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    // ¿Un cero inicial en el día? Por supuesto; ¿por qué no funcionaría?
    console.log( new Date('2026/01/02') );
    // Resultado: Date Fri Jan 02 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    // A menos, por supuesto, que separes el año, mes y fecha con guiones.
    // Entonces se equivoca con el _día_ (y además cambia la zona horaria, ¡qué locura!).
    console.log( new Date('2026-01-02') );
    // Resultado: Date Thu Jan 01 2026 19:00:00 GMT-0500 (hora estándar de Colombia)
    


    Date apesta.
    Fue copiado apresurada y descaradamente de Java en el coche de camino a la escuela y obtuvo todas las mismas respuestas incorrectas, hasta el nombre en la parte superior de la página: `Date` no representa una _fecha_, representa un _tiempo_.

    Internamente, las fechas se almacenan como valores numéricos llamados **valores de tiempo**: timestamps de Unix, divididos en 1,000 milisegundos — lo cual, bueno, sí, un tiempo Unix también necesariamente implica una fecha, claro, pero _aún así_: Date representa un tiempo, del cual puedes inferir una fecha.

    Esto se vuelve aún más frustrante cuando trabajas con aplicaciones que necesitan manejar múltiples zonas horarias (México, Colombia, Argentina, Chile, etc.) o cuando intentas parsear fechas en formato DD/MM/YYYY que es el estándar en nuestra región.

    // Timestamp Unix para el lunes, 4 de diciembre de 1995 12:00:00 AM GMT-05 (el día en que se anunció JavaScript):
    const timestamp = 818053200;
    
    console.log( new Date( timestamp * 1000 ) );
    // Resultado: Date Mon Dec 04 1995 00:00:00 GMT-0500 (hora estándar de Colombia)
    

    Palabras como “fecha” y “tiempo” significan cosas, pero, claro — _lo que sea, JavaScript_.

    Java deprecó _su_ `Date` allá por 1997, solo unos años después de que el `Date` de JavaScript fuera liberado en el mundo desprevenido; mientras tanto, hemos estado atados a este desastre desde entonces. Es salvajemente inconsistente cuando se trata de analizar fechas, como has visto hasta ahora aquí.

    No tiene sentido de zonas horarias más allá de la local y GMT, lo cual es un problema enorme para desarrolladores que trabajan con aplicaciones internacionales o que necesitan manejar usuarios en diferentes países (México tiene múltiples zonas horarias, Argentina y Chile tienen horario de verano, etc.).

    Y hablando de eso, `Date` _solo_ respeta el modelo de calendario gregoriano. No entiende en absoluto el concepto de horario de verano de manera consistente, lo cual— quiero decir, bueno, sí, igual, pero yo no estoy _hecho de computadoras_.

    Todas estas deficiencias hacen que sea excepcionalmente común usar bibliotecas de terceros como Moment.js, date-fns o Day.js para trabajar alrededor de todo esto, algunas de las cuales son absolutamente _masivas_; un drenaje de rendimiento que ha causado daño real y medible a la web, especialmente en dispositivos móviles.

    Ninguna de estas es mi problema principal con `Date`. Mi queja es sobre más que analizar o sintaxis o “ergonomía del desarrollador” o el impacto de rendimiento en toda la web de soluciones completamente necesarias o incluso la definición de la palabra “fecha”. Mi problema con `Date` es profundo en el alma. Mi problema con `Date` es que usarlo significa _desviarse de la naturaleza fundamental del tiempo mismo_.

    Todos los valores primitivos de JavaScript son **inmutables**, lo que significa que los valores mismos no pueden ser cambiados. El valor numérico `3` nunca puede representar nada más que el concepto de “tres” — no puedes hacer que `true` signifique algo diferente a “verdadero”. Estos son valores con significados concretos, inquebrantables, del mundo real. Sabemos qué es tres.
    No puede ser alguna otra cosa que no sea tres. Estos tipos de datos inmutables se almacenan **por valor**, lo que significa que una variable que representa el valor numérico `3` efectivamente “contiene” — y por lo tanto se comporta como — el valor numérico `3`.

    Cuando un valor inmutable se asigna a una variable, el motor de JavaScript crea una copia de ese valor y almacena la copia en memoria:

    const theNumber = 3;
    
    console.log( theNumber );
    // Resultado: 3

    Esto encaja bien con el modelo mental común para “una variable”: `theNumber` “contiene” `3`.

    Cuando inicializamos `theOtherNumber` con el valor vinculado a `theNumber`, ese modelo mental se mantiene: una vez más se crea un `3` y se almacena en memoria. `theOtherNumber` ahora puede pensarse como que contiene su propio `3` discreto.

    const theNumber = 3;
    const theOtherNumber = theNumber;
    
    console.log( theOtherNumber );
    // Resultado: 3;

    El valor de `theNumber` no cambia cuando alteramos el valor asociado con `theOtherNumber`, por supuesto — de nuevo, estamos trabajando con dos instancias discretas de `3`.

    const theNumber = 3;
    let theOtherNumber = theNumber;
    
    theOtherNumber = 5;
    
    console.log( theOtherNumber );
    // Resultado: 5;
    
    console.log( theNumber );
    // Resultado: 3

    Cuando cambias el valor vinculado a `theOtherNumber`, no estás cambiando el `3`, estás creando un nuevo valor numérico inmutable y vinculándolo en su lugar. De ahí un error cuando intentas manipular una variable declarada usando `const`:

    const theNumber = 3;
    
    theNumber = 5;
    // Resultado: Uncaught TypeError: invalid assignment to const 'theNumber'

    No puedes cambiar la vinculación de un `const`, y _definitivamente_ no puedes alterar el significado de `3`.

    Los tipos de datos que _pueden_ ser cambiados después de ser creados son **mutables**, lo que significa que el valor de datos _mismo_ puede ser alterado. Los valores de objeto — cualquier valor no primitivo, como un array, map o set — son mutables.

    Los objetos se almacenan **por referencia**, lo que significa que una variable que representa un objeto no contiene el objeto en sí, sino una referencia a la ubicación en memoria donde se almacena ese objeto. Cuando asignas un objeto a una variable, el motor de JavaScript almacena una referencia a ese objeto en memoria, no una copia del objeto mismo.

    const theObject = { value: 3 };
    
    console.log( theObject );
    // Resultado: { value: 3 }

    Cuando inicializamos `theOtherObject` con el valor vinculado a `theObject`, estamos creando una nueva referencia que apunta a la misma ubicación en memoria que `theObject`. Ambos `theObject` y `theOtherObject` ahora “apuntan” al mismo objeto en memoria.

    const theObject = { value: 3 };
    const theOtherObject = theObject;
    
    console.log( theOtherObject );
    // Resultado: { value: 3 };

    Cuando alteramos el objeto referenciado por `theOtherObject`, estamos alterando el mismo objeto en memoria que `theObject` también está referenciando. Ambos `theObject` y `theOtherObject` ahora apuntan a un objeto con un valor diferente.

    const theObject = { value: 3 };
    const theOtherObject = theObject;
    
    theOtherObject.value = 5;
    
    console.log( theOtherObject );
    // Resultado: { value: 5 };
    
    console.log( theObject );
    // Resultado: { value: 5 }

    Esto es lo que significa trabajar con valores mutables: cuando alteras el objeto, estás alterando el valor real que representa, no solo la variable que lo referencia.

    Y aquí está el problema con `Date`: `Date` es un objeto, lo que significa que es mutable. Cuando creas una instancia de `Date`, estás creando un objeto que representa un momento específico en el tiempo, y ese objeto puede ser alterado después de su creación.

    
    const today = new Date();
    
    console.log( today );
    // Resultado: Date Wed Dec 31 2025 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    const tomorrow = today;
    tomorrow.setDate( today.getDate() + 1 );
    
    console.log( tomorrow );
    // Resultado: Date Thu Jan 01 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    console.log( today );
    // Resultado: Date Thu Jan 01 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
    

    ¡Oh no! `today` también cambió, porque `tomorrow` y `today` son referencias al mismo objeto `Date` en memoria. Cuando llamamos `setDate` en `tomorrow`, estamos alterando el mismo objeto que `today` está referenciando.

    Esto es un problema fundamental. El tiempo no funciona así. El tiempo es inmutable. El 31 de diciembre de 2025 siempre será el 31 de diciembre de 2025.
    No puedes “cambiar” el 31 de diciembre de 2025 para que sea el 1 de enero de 2026 — esos son dos momentos diferentes en el tiempo, y no puedes hacer que uno se convierta en el otro.

    Imagínate esto en una aplicación de e-commerce donde calculas fechas de entrega, o en un sistema de reservas donde manejas fechas de check-in y check-out. Un bug así puede costarte clientes y dinero real.

    Pero `Date` te permite hacer exactamente eso. Puedes tomar un objeto `Date` que representa el 31 de diciembre de 2025 y “cambiarlo” para que represente el 1 de enero de 2026, y eso es simplemente incorrecto. Es una violación de cómo funciona el tiempo en el mundo real.

    const today = new Date();
    
    const addDay = theDate => {
    	theDate.setDate( theDate.getDate() + 1 );
    	return theDate;
    };
    
    console.log(`Mañana será ${ addDay( today ).toLocaleDateString() }. Hoy es ${ today.toLocaleDateString() }.`);
    // Resultado: Mañana será 1/1/2026. Hoy es 1/1/2026.
    

    Esto es un desastre. `today` y el resultado de `addDay( today )` son el mismo objeto, por lo que ambos muestran la misma fecha. Esto no es cómo debería funcionar el tiempo.

    El tiempo es inmutable. No puedes cambiar el pasado, no puedes cambiar el presente, y ciertamente no puedes cambiar el futuro simplemente alterando un objeto en memoria. El tiempo simplemente no funciona así.

    Y aquí está la cosa: esto no es solo un problema filosófico. Esto causa errores reales en código real. Es fácil crear accidentalmente múltiples referencias al mismo objeto `Date` y luego alterar ese objeto de maneras que afectan todas esas referencias, lo que lleva a bugs sutiles y difíciles de rastrear.

    Entra `Temporal`.

    `Temporal` es una propuesta para una nueva API de JavaScript para trabajar con fechas y tiempos. Es una reescritura completa de cómo JavaScript maneja el tiempo, y está diseñada para abordar todos los problemas con `Date` que hemos estado discutiendo.

    Lo más importante: `Temporal` es inmutable. Cuando creas un objeto `Temporal`, ese objeto representa un momento específico en el tiempo, y ese momento no puede ser cambiado. Cuando quieres representar un momento diferente en el tiempo, creas un nuevo objeto `Temporal`.

    const today = Temporal.Now.plainDateISO();
    
    console.log( today );
    // Resultado: Temporal.PlainDate 2025-12-31
    
    const tomorrow = today.add({ days: 1 });
    
    console.log( tomorrow );
    // Resultado: Temporal.PlainDate 2026-01-01
    
    console.log( today );
    // Resultado: Temporal.PlainDate 2025-12-31
    

    ¡Perfecto! `today` sigue siendo el 31 de diciembre de 2025, y `tomorrow` es el 1 de enero de 2026. No hay confusión, no hay efectos secundarios inesperados, no hay violación de la naturaleza fundamental del tiempo.

    `Temporal` también aborda muchos de los otros problemas con `Date`:

    **Análisis consistente**: `Temporal` tiene un análisis de fechas mucho más predecible y consistente que `Date`.

    **Soporte de zonas horarias**: `Temporal` tiene soporte completo para zonas horarias, no solo la zona horaria local y GMT.

    **Soporte de calendarios**: `Temporal` puede trabajar con diferentes sistemas de calendario, no solo el calendario gregoriano.

    **API más clara**: `Temporal` tiene una API mucho más clara e intuitiva que `Date`.

    Pero lo más importante es que `Temporal` respeta la naturaleza inmutable del tiempo. Cuando trabajas con `Temporal`, estás trabajando con valores que representan momentos específicos en el tiempo, y esos valores no pueden ser cambiados. Si quieres representar un momento diferente en el tiempo, creas un nuevo valor `Temporal`.

    Esto es cómo debería funcionar el tiempo en programación. El tiempo es inmutable en el mundo real, y debería ser inmutable en nuestro código también.

    `Temporal` todavía está en desarrollo, pero ya está disponible en las versiones más recientes de Chrome y Firefox. Pronto estará disponible en todos los navegadores principales, y finalmente podremos dejar `Date` atrás y usar una API de tiempo que realmente respete cómo funciona el tiempo.

    Para desarrolladores, esto significa poder trabajar con zonas horarias de manera nativa sin depender de bibliotecas pesadas, manejar formatos de fecha locales (DD/MM/YYYY) de forma consistente, y evitar esos bugs raros que aparecen cuando trabajas con fechas en aplicaciones internacionales.

    const today = Temporal.Now.plainDateISO();
    
    // Fecha local actual:
    console.log( today );
    /* Resultado (expandido):
    Temporal.PlainDate 2025-12-30
    	<prototype>: Object { … }
    */
    
    // Año local actual:
    console.log( today.year );
    // Resultado: 2025
    
    // Fecha y hora local actual:
    console.log( today.toPlainDateTime() );
    /* Resultado (expandido):
    Temporal.PlainDateTime 2025-12-30T00:00:00
    	<prototype>: Object { … }
    */
    
    // Especificar que esta fecha representa la zona horaria America/Mexico_City:
    console.log( today.toZonedDateTime( "America/Mexico_City" ) );
    /* Resultado (expandido):
    Temporal.ZonedDateTime 2025-12-30T00:00:00-06:00[America/Mexico_City]
    	<prototype>: Object { … }
    */
    
    // O trabajar con otras zonas horarias latinoamericanas:
    console.log( today.toZonedDateTime( "America/Bogota" ) ); // Colombia
    console.log( today.toZonedDateTime( "America/Buenos_Aires" ) ); // Argentina
    console.log( today.toZonedDateTime( "America/Santiago" ) ); // Chile
    
    // Agregar un día a esta fecha:
    console.log( today.add({ days: 1 }) );
    /*
    Temporal.PlainDate 2025-12-31
    	<prototype>: Object { … }
    */
    
    // Agregar un mes y un día a esta fecha, y restar dos años:
    console.log( today.add({ months: 1, days: 1 }).subtract({ years: 2 }) );
    /*
    Temporal.PlainDate 2024-01-31
    	<prototype>: Object { … }
    */
    
    console.log( today );
    /* Resultado (expandido):
    Temporal.PlainDate 2025-12-30
    	<prototype>: Object { … }
    */

    Observa cómo ninguna de estas transformaciones requirió que manualmente creáramos nuevos objetos, _y_ que el valor del objeto referenciado por `today` permanece sin cambios. A diferencia de `Date`, los métodos que usamos para interactuar con un objeto `Temporal` resultan en objetos `Temporal` _nuevos_, en lugar de requerir que los usemos en el contexto de una nueva instancia o modificar la instancia con la que estamos trabajando — que es cómo podemos encadenar los métodos `add` y `subtract` juntos en `today.add({ months: 1, days: 1 }).subtract({ years: 2 })`.

    Claro, todavía estamos trabajando con objetos, y eso significa que estamos trabajando con estructuras de datos mutables que representan valores del mundo real:

    const today = Temporal.Now.plainDateISO();
    
    today.someProperty = true;
    
    console.log( today );
    
    /* Resultado (expandido):
    Temporal.PlainDate 2026-01-05
    	someProperty: true
    	<prototype>: Object { … }
    */

    …Pero el valor representado por ese objeto `Temporal` no está destinado a ser cambiado durante el curso normal de interactuar con él — aunque el objeto sigue siendo esencialmente mutable, no estamos atrapados usando ese objeto de maneras que podrían alterar lo que significa en términos de fechas y tiempos del mundo real.
    Lo acepto.

    Entonces, revisemos ese pequeño script “hoy es X, mañana es Y” que escribimos usando `Date` anteriormente. Primero, lo arreglaremos asegurándonos de que estamos trabajando con dos instancias discretas de `Date` en lugar de modificar la instancia que representa la fecha de hoy:


    const today = new Date();
    
    const addDay = theDate => {
    	const tomorrow = new Date();
    
    	tomorrow.setDate( theDate.getDate() + 1 );
    	return tomorrow;
    };
    
    console.log(`Mañana será ${ addDay( today ).toLocaleDateString() }. Hoy es ${ today.toLocaleDateString() }.`);
    // Resultado: Mañana será 1/1/2026. Hoy es 12/31/2025.
    

    Gracias, lo odio.

    Bien, está bien. Cumple su función, tal como lo ha hecho desde el día en que `Date` se abrió paso por primera vez en la web. No estamos alterando sin saberlo el valor de `today` ya que estamos creando una nueva instancia de `Date` dentro de nuestra función `addDay` — verboso, pero funciona, como lo ha hecho durante décadas ahora. Le agregamos `1`, que tenemos que simplemente _saber_ que significa agregar un _día._

    Luego en nuestro template literal necesitamos seguir empujando a JavaScript para que nos dé la fecha en un formato que no incluya la hora actual, como una cadena. Es funcional, pero verboso.

    Ahora, rehagámoslo usando `Temporal`:

    const today = Temporal.Now.plainDateISO();
    
    console.log(`Mañana será ${ today.add({ days: 1 }) }. Hoy es ${ today }.`);
    // Resultado: Mañana será 2026-01-01. Hoy es 2025-12-31.
    

    Ahora sí estamos hablando.

    _Mucho mejor_. Más delgado, más eficiente, y _mucho_ menos margen para error. Queremos la fecha de hoy sin la hora, y el objeto que resulta de invocar `plainDateISO` (y cualquier nuevo objeto `Temporal` creado a partir de él) retendrá ese formato _sin_ ser coaccionado a una cadena.

    Formato: _verificado_.

    Queremos generar un valor que represente la fecha de hoy más un día, y queremos hacerlo de una manera donde estamos diciendo inequívocamente “agregar un día” sin conjeturas de análisis: _verificado_ y _verificado_.

    Lo más importante, no queremos correr el riesgo de que nuestro objeto `today` original sea alterado sin intención — porque el resultado de llamar al método `add` siempre será un nuevo objeto `Temporal`: _verificado_.

    `Temporal` va a ser una _mejora masiva_ sobre `Date`, y solo digo “va a ser” porque todavía no está completamente listo para uso en producción.

    La especificación de borrador para el objeto `Temporal` propuesto ha alcanzado la etapa tres del proceso de estandarización, lo que significa que ahora está oficialmente “recomendado para implementación” — aún no es parte del estándar que informa el desarrollo continuo de JavaScript mismo, pero lo suficientemente cerca como para que los navegadores puedan empezar a experimentar con él.

    Eso significa que los resultados de esa experimentación temprana pueden usarse para refinar aún más la especificación, por lo que nada está escrito en piedra todavía. Los estándares web son un proceso iterativo, después de todo.

    Ahí es donde entramos tú y yo. Ahora que `Temporal` ha llegado a las versiones más recientes de Chrome y Firefox — y otros, pronto — es hora de que entremos y probemos un poco. Puede que no hayamos tenido ninguna opinión sobre `Date`, pero podemos experimentar con `Temporal` antes de que lleguen las implementaciones finales.

    Pronto, JavaScript tendrá un manejo de fechas sensato y moderno, y finalmente podremos meter `Date` muy atrás en el cajón de chatarra con las bandas elásticas, tapas de frascos sin pareja, llaves misteriosas y probablemente pilas AA medio vacías — todavía presente, todavía una parte inexorable de la plataforma web, pero ya no nuestra primera, última y única forma de manejar fechas.

    Y solo tuvimos que esperar— bueno, espera, déjame calcular los números rápidamente con `Temporal`:

    Pruébalo

    const today = Temporal.Now.plainDateISO();
    const jsShipped = Temporal.PlainDate.from( "1995-12-04" );
    const sinceDate = today.since( jsShipped, { largestUnit: 'year' });
    
    console.log( `${ sinceDate.years } años, ${ sinceDate.months } meses, y ${ sinceDate.days } días.` );
    

    Ejecutar

    Claro, el mejor momento para reemplazar `Date` habría sido allá por 1995, pero oye: el segundo mejor momento es `Temporal.Now`, ¿verdad?

    ## ¿Por Qué Esto Importa para Desarrolladores en Latinoamérica?

    Como desarrolladores latinoamericanos, trabajamos constantemente con:

    **Aplicaciones internacionales** que necesitan manejar múltiples zonas horarias

    **Formatos de fecha locales** (DD/MM/YYYY) que `Date` maneja de forma inconsistente

    **Dispositivos móviles** donde el rendimiento importa y las bibliotecas pesadas de fechas afectan la experiencia del usuario

    **E-commerce y sistemas de reservas** donde un error con fechas puede costar dinero real

    `Temporal` viene a resolver todos estos problemas de forma nativa, sin necesidad de bibliotecas externas pesadas. Es hora de empezar a experimentar con esta nueva API y prepararnos para el futuro del manejo de fechas en JavaScript.

    **¿Te resultó útil este artículo?** Compártelo con otros desarrolladores que también están luchando con `Date`. Y si quieres profundizar más en JavaScript moderno, asegúrate de seguir aprendiendo sobre las nuevas características del lenguaje.