requestIdleCallback: Ejecuta Tareas de Baja Prioridad en Segundo Plano
Aprende a ejecutar tareas no críticas cuando el navegador está inactivo para mejorar el rendimiento y la experiencia de usuario.
TL;DR - Resumen rápido
- requestIdleCallback ejecuta código cuando el navegador está inactivo y no hay tareas críticas pendientes
- El callback recibe un objeto deadline con timeRemaining() para saber cuánto tiempo disponible queda
- Es ideal para tareas de baja prioridad como analíticas, pre-fetching y limpieza de datos
- Devuelve un ID único que puedes usar con cancelIdleCallback para cancelar la tarea
- No es adecuado para animaciones o tareas que deben ejecutarse inmediatamente
Introducción a requestIdleCallback
En aplicaciones web modernas, a menudo necesitamos ejecutar tareas que no son críticas para la experiencia de usuario inmediata. Estas tareas pueden incluir enviar analíticas, pre-fetching de recursos, limpieza de datos en caché, o procesamiento de grandes cantidades de información. Ejecutar estas tareas en el momento equivocado puede bloquear el hilo principal y causar jank (animaciones entrecortadas) o interacciones lentas. requestIdleCallback resuelve este problema permitiéndote programar tareas de baja prioridad que se ejecutan solo cuando el navegador está inactivo.
Esta API fue diseñada específicamente para mejorar el rendimiento percibido por el usuario. Mientras requestAnimationFrame está optimizado para tareas visuales que deben ejecutarse antes de cada repintado, requestIdleCallback es lo opuesto: está diseñado para tareas que pueden esperar hasta que el navegador no tenga nada más importante que hacer. Esto es especialmente útil en dispositivos móviles con recursos limitados, donde cada milisegundo de CPU cuenta para la batería y la fluidez de la UI.
¿Qué es requestIdleCallback?
requestIdleCallback es un método del objeto window que te permite programar una función para que se ejecute durante los periodos de inactividad del navegador. A diferencia de setTimeout, que ejecuta código después de un tiempo específico independientemente de la carga del sistema, requestIdleCallback espera inteligentemente hasta que el navegador determine que hay tiempo libre disponible sin afectar la experiencia del usuario.
- <strong>Ejecución oportunista:</strong> Solo se ejecuta cuando el navegador está inactivo
- <strong>Deadline dinámico:</strong> Recibe información sobre cuánto tiempo disponible queda
- <strong>Cancelación:</strong> Devuelve un ID único para cancelar la tarea si es necesario
- <strong>Prioridad baja:</strong> Siempre se ejecuta después de tareas críticas y animaciones
- <strong>Compatibilidad:</strong> Soportado en Chrome, Edge y Firefox (polyfill disponible para Safari)
¿Cuándo se considera el navegador inactivo?
El navegador considera que está inactivo cuando ha completado todas las tareas críticas del frame actual y todavía tiene tiempo disponible antes del próximo repintado. Esto suele ocurrir en periodos breves de unos pocos milisegundos entre frames, o cuando el usuario no está interactuando activamente con la página. requestIdleCallback aprovecha estos micro-momentos para ejecutar tareas de baja prioridad.
Compatibilidad y polyfill
requestIdleCallback es compatible con Chrome, Edge y Firefox. Safari no lo soporta nativamente, pero puedes usar un polyfill basado en setTimeout. Para aplicaciones en producción, verifica la compatibilidad con if ('requestIdleCallback' in window) antes de usarlo, o usa una implementación de respaldo con setTimeout.
Uso Básico
El uso básico de requestIdleCallback es similar a requestAnimationFrame: pasas una función de callback que se ejecutará cuando el navegador esté inactivo. La diferencia clave es que este callback recibe un objeto con información sobre el deadline y si el callback se ejecutó debido a un timeout.
Callback Simple
Este ejemplo muestra cómo usar requestIdleCallback para ejecutar una tarea de baja prioridad. La función se ejecutará cuando el navegador tenga tiempo libre disponible.
En este ejemplo, programamos una tarea de analítica que se ejecutará cuando el navegador esté inactivo. El callback recibe un objeto deadline que contiene el método timeRemaining(), que indica cuántos milisegundos quedan disponibles antes de que el navegador necesite volver a procesar tareas críticas. Usamos este valor para verificar si tenemos suficiente tiempo para completar la tarea.
Timeout Opcional
Puedes especificar un timeout opcional para garantizar que el callback se ejecute después de un tiempo máximo, incluso si el navegador nunca ha estado completamente inactivo. Esto es útil para tareas que deben ejecutarse eventualmente, pero pueden esperar un tiempo razonable.
El segundo parámetro de requestIdleCallback es un objeto de opciones donde puedes especificar un timeout en milisegundos. En este ejemplo, el callback se ejecutará dentro de 2000ms máximo, incluso si el navegador nunca ha estado completamente inactivo. Cuando el callback se ejecuta debido al timeout, la propiedad didTimeout del objeto deadline será true, indicando que no debes confiar en timeRemaining() para saber cuánto tiempo tienes disponible.
Advertencia sobre timeouts
No uses timeouts muy cortos (menos de 50ms) porque esto derrotaría el propósito de requestIdleCallback. Si necesitas que algo se ejecute rápidamente, usa setTimeout directamente. requestIdleCallback con timeout es para tareas que pueden esperar, pero que deben ejecutarse eventualmente dentro de un tiempo razonable.
Deadline y Timings
El objeto deadline que recibe el callback contiene información crítica para gestionar el tiempo disponible. Comprender cómo funciona timeRemaining() y didTimeout es esencial para usar requestIdleCallback de manera efectiva y evitar bloquear el hilo principal.
Usar el Deadline Correctamente
Este ejemplo muestra cómo usar timeRemaining() para dividir tareas grandes en chunks más pequeños que pueden ejecutarse en múltiples periodos de inactividad.
Este patrón es muy útil para procesar grandes conjuntos de datos sin bloquear el hilo principal. Verificamos timeRemaining() antes de procesar cada elemento. Si el tiempo disponible es menor que el umbral (en este caso 1ms), volvemos a llamar a requestIdleCallback para continuar en el próximo periodo de inactividad. Esto permite que el navegador siga respondiendo a interacciones del usuario mientras procesamos la tarea en segundo plano.
Mejor práctica: tiempo de seguridad
Siempre deja un margen de seguridad cuando uses timeRemaining(). No uses todo el tiempo disponible porque el navegador necesita un poco de tiempo para prepararse para el próximo frame. Un margen de 1-2ms es generalmente suficiente. Esto ayuda a prevenir que tu tarea cause jank en la siguiente animación o interacción del usuario.
Cancelar Callbacks
Al igual que con requestAnimationFrame, requestIdleCallback devuelve un identificador único que puedes usar para cancelar el callback si ya no es necesario. Esto es importante para evitar que tareas de baja prioridad se ejecuten después de que el usuario haya navegado a otra página o cerrado un componente.
Cancelación Básica
Este ejemplo muestra cómo cancelar un callback de requestIdleCallback cuando el usuario navega a otra página o cuando un componente se desmonta.
Guardamos el ID devuelto por requestIdleCallback en una variable. Cuando el usuario hace clic en el botón de cancelar, usamos cancelIdleCallback con ese ID para detener la tarea pendiente. Es especialmente importante cancelar callbacks cuando el usuario abandona la página, porque de lo contrario la tarea podría ejecutarse después de que la página ya no esté visible, consumiendo recursos innecesarios.
Casos de Uso Prácticos
requestIdleCallback es ideal para una variedad de tareas que no son críticas para la experiencia inmediata del usuario. Conocer estos casos de uso te ayudará a identificar cuándo usar esta API en tus propias aplicaciones.
- <strong>Analítica y telemetría:</strong> Enviar datos de uso a servidores sin afectar la interactividad
- <strong>Pre-fetching:</strong> Cargar recursos que el usuario probablemente necesitará en el futuro
- <strong>Limpieza de caché:</strong> Eliminar datos antiguos o expirados de localStorage/IndexedDB
- <strong>Procesamiento de datos:</strong> Analizar grandes conjuntos de datos en segundo plano
- <strong>Sincronización:</strong> Enviar cambios pendientes al servidor cuando el sistema está inactivo
Enviar Analíticas
Este ejemplo muestra cómo usar requestIdleCallback para enviar datos de analítica sin afectar el rendimiento de la página.
Este patrón es muy común en aplicaciones web que necesitan enviar datos de analítica. En lugar de enviar los datos inmediatamente cuando ocurre un evento (lo que podría afectar el rendimiento), acumulamos los eventos en una cola y los enviamos en lotes cuando el navegador está inactivo. Esto reduce el número de solicitudes de red y minimiza el impacto en la experiencia del usuario.
Pre-fetching de Recursos
Este ejemplo muestra cómo pre-fetch recursos que el usuario probablemente necesitará, cargándolos cuando el navegador está inactivo.
El pre-fetching inteligente puede mejorar significativamente el tiempo de carga de páginas subsecuentes. En este ejemplo, identificamos enlaces que el usuario probablemente hará clic y pre-cargamos esos recursos cuando el navegador está inactivo. Esto hace que la navegación entre páginas sea casi instantánea para el usuario.
requestIdleCallback vs Web Workers
requestIdleCallback ejecuta código en el hilo principal durante periodos de inactividad, ideal para tareas ligeras de baja prioridad. Web Workers ejecutan código en un hilo separado en paralelo, ideal para tareas pesadas y computacionalmente intensivas. Usa requestIdleCallback para analíticas y pre-fetching; usa Web Workers para procesamiento de imágenes, cálculos complejos o parsing de datos grandes.
Errores Comunes
Al usar requestIdleCallback, hay varios errores que los desarrolladores cometen frecuentemente. Evitar estos patrones problemáticos te ayudará a usar esta API de manera efectiva.
Error: Usar para Tareas Críticas
Un error común es usar requestIdleCallback para tareas que son críticas para la experiencia inmediata del usuario. Esto puede causar que tareas importantes se ejecuten tarde o nunca.
En este ejemplo, estamos usando requestIdleCallback para mostrar un mensaje de éxito después de que el usuario envía un formulario. Esto es problemático porque el mensaje podría aparecer mucho después de que el usuario envíe el formulario, o incluso nunca si el navegador nunca está completamente inactivo. Los mensajes de feedback del usuario deben mostrarse inmediatamente usando setTimeout o directamente en el callback de la acción.
La solución es mostrar el mensaje de éxito inmediatamente usando setTimeout (o directamente en el callback), y usar requestIdleCallback solo para tareas que pueden esperar, como enviar analíticas sobre el envío del formulario.
Error: No Verificar el Deadline
Otro error común es no verificar timeRemaining() antes de ejecutar tareas largas, lo que puede causar que el callback bloquee el hilo principal más tiempo del permitido.
Este código procesa todos los elementos del array sin verificar cuánto tiempo disponible queda. Si el array es grande, esto puede bloquear el hilo principal por varios segundos, causando jank y haciendo que la página parezca congelada. El propósito de requestIdleCallback es evitar exactamente este tipo de problema.
La solución verifica timeRemaining() antes de procesar cada elemento y continúa en el próximo periodo de inactividad si no hay suficiente tiempo disponible. Esto divide la tarea en chunks más pequeños que no bloquean el hilo principal.
Resumen: requestIdleCallback
Conceptos principales:
- •requestIdleCallback ejecuta tareas de baja prioridad cuando el navegador está inactivo
- •El callback recibe un objeto deadline con timeRemaining() y didTimeout
- •Puedes especificar un timeout opcional para garantizar ejecución eventual
- •Devuelve un ID único que se usa con cancelIdleCallback para cancelar la tarea
- •Es ideal para analíticas, pre-fetching, limpieza de caché y procesamiento de datos
Mejores prácticas:
- •Usa timeRemaining() para dividir tareas grandes en chunks más pequeños
- •Deja un margen de seguridad de 1-2ms cuando uses timeRemaining()
- •No uses requestIdleCallback para tareas críticas o feedback inmediato al usuario
- •Cancela callbacks cuando el usuario abandone la página o un componente se desmonte
- •Usa timeouts razonables (mínimo 50ms) para evitar derrotar el propósito de la API