# Issue #005: Eliminar timeouts innecesarios del código **Tipo**: Improvement **Prioridad**: Alta **Estado**: Pendiente ## Descripción Eliminar todos los `time.Sleep()` y timeouts hardcodeados innecesarios del código, reemplazándolos con esperas basadas en eventos CDP cuando sea posible. ## Problema actual El código tiene múltiples `time.Sleep()` arbitrarios: - `time.Sleep(2 * time.Second)` en examples/basic.go - `time.Sleep(3 * time.Second)` en cmd/list_blog.go - Timeouts hardcodeados en navegación Estos timeouts son problemáticos porque: - No se adaptan a velocidad real de carga - Desperdicían tiempo en conexiones rápidas - Fallan en conexiones lentas - Hacen el código menos robusto ## Estrategia de reemplazo ### 1. Eventos CDP de carga de página En lugar de: ```go b.Navigate(ctx, url, nil) time.Sleep(3 * time.Second) ``` Usar eventos CDP: ```go opts := browser.DefaultNavigateOptions() opts.WaitUntil = "networkidle" // o "load" o "domcontentloaded" b.Navigate(ctx, url, opts) // No sleep necesario, Navigate espera el evento ``` ### 2. Esperar por selectores En lugar de: ```go time.Sleep(2 * time.Second) html, _ := b.GetHTML(ctx, ".content") ``` Usar: ```go b.WaitForSelector(ctx, ".content", 30*time.Second) html, _ := b.GetHTML(ctx, ".content") ``` ### 3. Esperar por condiciones JavaScript En lugar de: ```go time.Sleep(1 * time.Second) result, _ := b.Evaluate(ctx, "window.dataReady") ``` Usar: ```go b.WaitForFunction(ctx, "window.dataReady === true", 100*time.Millisecond) result, _ := b.Evaluate(ctx, "window.data") ``` ### 4. Eventos de red Esperar que network esté idle: ```go // Implementar WaitForNetworkIdle() b.WaitForNetworkIdle(ctx, 500*time.Millisecond, 30*time.Second) ``` ## Eventos CDP útiles ### Page domain - `Page.loadEventFired` - Página cargada completamente - `Page.domContentEventFired` - DOM listo - `Page.frameStoppedLoading` - Frame dejó de cargar ### Network domain - `Network.requestWillBeSent` - Request iniciado - `Network.responseReceived` - Response recibida - `Network.loadingFinished` - Recurso terminó de cargar - `Network.loadingFailed` - Recurso falló ## Métodos a implementar ```go // WaitForEvent espera un evento CDP específico func (b *Browser) WaitForEvent(ctx context.Context, eventName string, timeout time.Duration) error // WaitForNetworkIdle espera que no haya requests de red por X tiempo func (b *Browser) WaitForNetworkIdle(ctx context.Context, idleTime, timeout time.Duration) error // WaitForFunction espera que una función JS retorne true func (b *Browser) WaitForFunction(ctx context.Context, fn string, checkInterval time.Duration) error // WaitForNavigation espera que navegación complete func (b *Browser) WaitForNavigation(ctx context.Context, timeout time.Duration) error ``` ## Archivos a revisar y actualizar - [x] `examples/basic.go` - Eliminar time.Sleep - [x] `examples/advanced.go` - Reemplazar con esperas basadas en eventos - [x] `cmd/list_blog.go` - Usar WaitForSelector - [ ] `pkg/browser/navigation.go` - Mejorar Navigate() para esperar eventos - [ ] `pkg/browser/browser.go` - Agregar métodos de espera ## Implementación en Navigate() ```go func (b *Browser) Navigate(ctx context.Context, url string, opts *NavigateOptions) error { if opts == nil { opts = DefaultNavigateOptions() } // Registrar listener ANTES de navegar loadedChan := make(chan struct{}) b.client.On("Page.loadEventFired", func() { close(loadedChan) }) // Enviar comando de navegación _, err := b.client.SendCommand(ctx, "Page.navigate", map[string]interface{}{ "url": url, }) if err != nil { return err } // Esperar evento según opts.WaitUntil select { case <-loadedChan: return nil case <-ctx.Done(): return ctx.Err() } } ``` ## Beneficios ✅ **Más rápido**: No espera más de lo necesario ✅ **Más robusto**: Falla con timeout claro, no con misterioso "elemento no encontrado" ✅ **Más confiable**: Se adapta a velocidad real de página ✅ **Mejor UX**: Feedback claro de qué se está esperando ## Testing Probar con: - Conexiones rápidas (localhost) - Conexiones lentas (throttling) - Páginas con mucho JavaScript - Páginas con assets pesados - SPAs (React, Vue) que cargan async ## Referencias - CDP Page events: https://chromedevtools.github.io/devtools-protocol/tot/Page/#event-loadEventFired - CDP Network events: https://chromedevtools.github.io/devtools-protocol/tot/Network/ - Puppeteer waitFor: https://pptr.dev/guides/page-interactions#waiting