8742cb25be
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
76 lines
4.8 KiB
Markdown
76 lines
4.8 KiB
Markdown
---
|
|
name: cdp_wait_idle
|
|
kind: function
|
|
lang: go
|
|
domain: browser
|
|
version: "1.2.0"
|
|
purity: impure
|
|
signature: "func CdpWaitIdle(c *CDPConn, opts CdpWaitIdleOpts) error"
|
|
description: "Espera a que la actividad de red de la pagina llegue a idle usando eventos CDP Network.*. Lleva un contador de requests en vuelo (inflight) via InflightTracker: trackea por requestId, excluye conexiones persistentes (WebSocket, EventSource) que nunca terminan. Cuando inflight <= MaxInflight (default 2) de forma continuada durante QuietMs ms, retorna nil. Inmune a extensiones que mutan el DOM (Dark Reader, uBlock) y a animaciones JS. Si se alcanza Timeout sin lograr la ventana quieta, retorna error con el inflight actual."
|
|
tags: [cdp, chrome, browser, wait, spa, network, idle, polling, hydration, navegator]
|
|
uses_functions: []
|
|
uses_types: []
|
|
returns: []
|
|
returns_optional: false
|
|
error_type: "error_go_core"
|
|
imports: [fmt, sync, time]
|
|
params:
|
|
- name: c
|
|
desc: "conexion CDP activa (obtenida con CdpConnect)"
|
|
- name: opts
|
|
desc: "opciones de espera: QuietMs ms de red quieta (default 500), Timeout maximo total (default 8s), MaxInflight requests en vuelo tolerados para considerar idle (default 2), PollMs intervalo de chequeo (default 100). Campos a 0 usan el default."
|
|
output: "nil si la red llega a idle dentro del timeout; error descriptivo con inflight actual si se agota el tiempo o la conexion falla"
|
|
tested: true
|
|
tests:
|
|
- "TestCdpWaitIdleDefaults"
|
|
- "TestInflightTracker"
|
|
test_file_path: "functions/browser/cdp_wait_idle_test.go"
|
|
file_path: "functions/browser/cdp_wait_idle.go"
|
|
---
|
|
|
|
## Ejemplo
|
|
|
|
```go
|
|
conn, _ := CdpConnect(9222)
|
|
CdpNavigate(conn, "https://my-spa.com/dashboard")
|
|
|
|
// Esperar readyState=complete primero.
|
|
_ = CdpWaitLoad(conn, 30*time.Second)
|
|
|
|
// Luego esperar a que la red quede idle (sin requests en vuelo).
|
|
if err := CdpWaitIdle(conn, CdpWaitIdleOpts{
|
|
QuietMs: 500, // 500 ms sin requests en vuelo
|
|
Timeout: 8 * time.Second,
|
|
MaxInflight: 0, // 0 = idle absoluto; 1+ = tolera polling/WS
|
|
PollMs: 100,
|
|
}); err != nil {
|
|
log.Fatal("red no llego a idle:", err)
|
|
}
|
|
|
|
html, _ := CdpGetHTML(conn)
|
|
```
|
|
|
|
## Cuando usarla
|
|
|
|
Cuando `CdpWaitLoad` no basta porque la SPA lanza fetch/XHR adicionales tras `readyState=complete` y necesitas esperar a que terminen antes de extraer HTML o hacer clicks. Usar justo despues de `CdpWaitLoad` o de `CdpNavigate`.
|
|
|
|
Preferir esta funcion sobre la version DOM-length anterior cuando la pagina tenga extensiones activas (Dark Reader, uBlock) o animaciones JS que mutan el DOM continuamente: esas fuentes de ruido no afectan el contador de red.
|
|
|
|
## Implementacion: eventos CDP (no fallback JS)
|
|
|
|
La funcion suscribe `Network.requestWillBeSent`, `Network.loadingFinished` y `Network.loadingFailed` usando `c.OnEvent`, el mismo mecanismo que `cdp_har_record`. CDPConn soporta multiples consumidores por metodo (slice de handlers), por lo que esta funcion y `cdp_har_record` pueden usarse en paralelo sobre la misma conexion sin conflicto. El fallback JS (`window.__fn_inflight` via XHR/fetch hook) no fue necesario.
|
|
|
|
## Gotchas
|
|
|
|
- **MaxInflight default = 2**: la web moderna mantiene 1-2 beacons/analytics de fondo que rara vez dejan inflight en 0. El zero-value de `MaxInflight` (0) se reescribe a 2 para no colgar hasta el timeout. Para exigir idle absoluto en una página simple, no hay valor de "0 explícito" (0 == default); usa una página sin analytics o asume el umbral 2.
|
|
- **WebSocket / EventSource excluidos del conteo**: estas conexiones persistentes no emiten `loadingFinished`, así que contarlas dejaría inflight clavado para siempre. El `InflightTracker` las ignora en `requestWillBeSent` (por `params.type`). Un stream WS/SSE abierto ya NO impide llegar a idle.
|
|
- **Polling/long-poll periódico**: si la página lanza un XHR cada N segundos, inflight oscila; con `MaxInflight: 2` (default) suele tolerarse. Si no, reduce `QuietMs` (ej. 200 ms) para capturar la ventana entre polls.
|
|
- **Error incluye inflight actual**: el mensaje de timeout incluye `inflight=N` para diagnóstico.
|
|
- **Network.enable/disable**: la función habilita Network al entrar y lo deshabilita al salir via defer. No interleave con `cdp_har_record` en la misma conexión salvo orden de cierre controlado.
|
|
- **Tests sin Chrome**: el núcleo (`InflightTracker`) se testea con secuencias de eventos sintéticas. El bucle de polling con timeout real requiere Chrome y no está simulado.
|
|
|
|
## Capability growth log
|
|
|
|
- v1.2.0 (2026-06-06) — refactor a `InflightTracker` puro (testeable sin red); default MaxInflight 0→2 (analytics ya no cuelga); excluye WebSocket/EventSource del conteo (no terminan); tracking por requestId (finish de request no contado = no-op).
|
|
- v1.1.0 (2026-06-05) — cambia señal DOM-length → network-idle via eventos CDP Network.*; añade MaxInflight configurable; defaults mas ajustados (QuietMs 800→500, Timeout 15s→8s, PollMs 200→100).
|