wandres.dev
NETWORK I · Leer el waterfall

El desglose del timing: las ocho fases de una petición

Qué ocurre en cada tramo de la barra de una petición, qué causa que cada uno se alargue, y cómo leer los números del desglose para saber a quién llamar.

⏱ 18 min

La barra que el panel dibuja para cada petición no es una barra: es la suma de hasta ocho tramos con causas completamente distintas. Un segundo de espera puede significar que el servidor es lento, que el navegador no tenía conexiones libres, que la resolución de nombres tardó, o que el fichero es enorme y la descarga es larga. Son cuatro problemas con cuatro responsables distintos, y el desglose los separa. Leerlo bien es la diferencia entre saber a quién llamar y ponerse a adivinar.

🎯 Al terminar esta lección sabrás
  • Enumerar las fases del timing de una petición y explicar qué ocurre en cada una.
  • Identificar la causa habitual de que cada fase se alargue.
  • Distinguir un problema de servidor de uno de conexión y de uno de tamaño.
  • Consultar el mismo desglose desde la consola con la API de rendimiento.

Las fases

flowchart TB
a[Peticion iniciada] --> b[En cola]
b --> c[Estancado]
c --> d{Hay conexion reutilizable}
d -->|No| e[Resolucion DNS]
e --> f[Conexion inicial]
f --> g[Negociacion TLS]
g --> h[Envio de la peticion]
d -->|Si| h
h --> i[Espera de respuesta TTFB]
i --> j[Descarga del contenido]
j --> k[Completada]
style a fill:#cba6f7,color:#11111b
style b fill:#f9e2af,color:#11111b
style c fill:#f9e2af,color:#11111b
style e fill:#89b4fa,color:#11111b
style f fill:#89b4fa,color:#11111b
style g fill:#89b4fa,color:#11111b
style h fill:#94e2d5,color:#11111b
style i fill:#f38ba8,color:#11111b
style j fill:#fab387,color:#11111b
style k fill:#a6e3a1,color:#11111b

En cola. La petición existe pero el navegador todavía no ha empezado a atenderla. Las causas son tres: hay peticiones de mayor prioridad por delante; no hay conexiones disponibles para ese origen; o el navegador está reservando espacio en la caché de disco antes de empezar.

Estancado. Tiempo entre el momento en que la petición podría salir y el momento en que sale de verdad. Se solapa conceptualmente con la cola y suele deberse al límite de conexiones o a la negociación con un proxy.

Resolución de nombres. Traducir el nombre del dominio a una dirección. Solo ocurre la primera vez para cada dominio; después está en caché. Un valor alto aquí, repetido en varios dominios, indica muchos orígenes distintos.

Conexión inicial. Establecer la conexión de transporte, con su intercambio de mensajes inicial. Depende directamente de la latencia de ida y vuelta.

Negociación segura. El apretón de manos criptográfico. Añade al menos un viaje de ida y vuelta más, y con configuraciones antiguas, dos.

Envío de la petición. Escribir la petición en la conexión. Normalmente es despreciable, salvo en subidas de ficheros, donde este tramo es toda la subida.

Espera de la respuesta. Desde que la petición sale hasta que llega el primer byte. Es el tiempo hasta el primer byte, y contiene dos cosas mezcladas: la latencia de red de ida y vuelta, y el tiempo que el servidor tardó en empezar a responder.

Descarga del contenido. Desde el primer byte hasta el último. Depende del tamaño y del ancho de banda.

ℹ️
Nota

Cuando hay un service worker, aparecen dos fases adicionales antes de todo: el arranque del worker si estaba dormido, y el tiempo que tarda en decidir qué hacer con la petición. Si el worker responde desde caché, todas las fases de red desaparecen.

Qué significa que cada fase sea larga

Fase larga Causa habitual A quién corresponde
En cola o estancado Límite de conexiones, demasiadas peticiones simultáneas Frontend: reducir peticiones o migrar de protocolo
Resolución de nombres Muchos dominios distintos, DNS lento Infraestructura, o reducir orígenes
Conexión inicial Latencia alta, servidor lejos Infraestructura: acercar con CDN
Negociación segura Cadena de certificados larga, configuración antigua Infraestructura
Envío Cuerpo de petición grande Frontend: es una subida
Espera de respuesta Servidor lento, consulta pesada, sin caché Backend
Descarga Fichero grande, ancho de banda bajo Frontend: comprimir, dividir, optimizar

Esa tabla es la que convierte el panel en una herramienta de conversación. Cuando alguien dice que la página va lenta, poder decir “el tiempo hasta el primer byte de esta llamada son mil doscientos milisegundos y todo lo demás está en veinte” cierra la discusión y asigna el trabajo.

Descomponer el tiempo de espera

El tramo de espera merece un análisis aparte porque mezcla dos cosas que conviene separar: la latencia de red y el trabajo del servidor.

La forma de separarlas es comparar con una petición trivial al mismo origen. Si pedir un recurso estático pequeño del mismo servidor tiene un tiempo de espera de ochenta milisegundos y tu llamada a la API tiene mil doscientos, la latencia de red son esos ochenta y el servidor tardó mil ciento veinte. Si el recurso estático también tarda ochocientos, el problema es de red o de infraestructura y no de tu consulta.

Hay además una cabecera estándar, Server-Timing, que permite al servidor reportar su propio desglose interno, y las DevTools la muestran dentro del desglose de la petición. Si tu backend la emite, la conversación deja de ser una negociación y pasa a ser un dato.

El mismo desglose desde la consola

Toda esta información está disponible programáticamente, lo que permite auditar decenas de peticiones de golpe.

// Desglose del timing de todas las peticiones, en una tabla
(() => {
  const filas = performance.getEntriesByType('resource').map(e => {
    const f = n => Math.round(n) || 0;
    return {
      recurso: e.name.split('/').pop().split('?')[0].slice(0, 32) || e.name,
      protocolo: e.nextHopProtocol || '',
      cola: f(e.domainLookupStart - e.startTime),
      dns: f(e.domainLookupEnd - e.domainLookupStart),
      conexion: f(e.secureConnectionStart > 0 ? e.secureConnectionStart - e.connectStart : e.connectEnd - e.connectStart),
      tls: f(e.secureConnectionStart > 0 ? e.connectEnd - e.secureConnectionStart : 0),
      espera: f(e.responseStart - e.requestStart),
      descarga: f(e.responseEnd - e.responseStart),
      total: f(e.duration)
    };
  }).sort((a, b) => b.total - a.total);
  console.table(filas.slice(0, 25));

  const suma = c => filas.reduce((s, f) => s + f[c], 0);
  console.table([{
    colaTotal: suma('cola'), dnsTotal: suma('dns'), conexionTotal: suma('conexion'),
    tlsTotal: suma('tls'), esperaTotal: suma('espera'), descargaTotal: suma('descarga')
  }]);
})();

La segunda tabla es la interesante: dice en qué se ha ido el tiempo agregado de toda la página. Si el total de espera es diez veces mayor que el de descarga, ninguna optimización de tamaño va a servir de nada.

Una petición rápida en una conexión ya abierta y la misma petición en frío no se parecen en nada

Hay una asimetría en este desglose que explica una parte enorme de los malentendidos sobre rendimiento de red, y es que las fases de establecimiento se pagan una vez y las de transferencia se pagan siempre. La primera petición a un dominio nuevo paga resolución de nombres, conexión y negociación segura: entre uno y tres viajes de ida y vuelta antes de que se envíe un solo byte útil. Con una latencia de cien milisegundos —perfectamente normal en móvil— eso son entre cien y trescientos milisegundos de coste fijo, antes de que el servidor se entere siquiera de qué le estás pidiendo. La segunda petición al mismo dominio reutiliza la conexión y ese coste desaparece por completo. La consecuencia práctica va en contra de una intuición muy extendida: el número de dominios distintos importa más que el número de peticiones. Cuarenta peticiones a un solo origen sobre una conexión multiplexada son baratas; ocho peticiones repartidas entre ocho dominios distintos pagan ocho establecimientos completos y pueden ser bastante más lentas. Eso invierte una práctica que fue estándar durante años, la de repartir recursos entre varios subdominios para saltarse el límite de seis conexiones por origen: con la primera generación del protocolo era una buena idea, y con la segunda es contraproducente, porque cada dominio adicional cuesta un establecimiento y la multiplexación ya elimina el límite. Y hay una segunda consecuencia que decide dónde mirar: en tu portátil, con la latencia de tu oficina, estas fases son casi invisibles; en el móvil de un usuario en una red celular, con latencias de doscientos milisegundos, dominan el tiempo total. Si mides el rendimiento de red sin simular latencia, estás midiendo precisamente el escenario en el que el problema no existe. Por eso el throttling no es un lujo para casos raros: es la única forma de que estos tramos aparezcan en tu pantalla con el tamaño que tienen en la realidad.