Envío de Datos en Segundo Plano con la API Beacon

La navegación de páginas no debería estar secuestrada por solicitudes de analíticas. Cuando los usuarios hacen clic en un enlace o cierran una pestaña, las solicitudes HTTP tradicionales pueden bloquearse o fallar completamente, dejándote con datos incompletos y usuarios frustrados. La solución de la API Beacon cambia esto al poner en cola las solicitudes en segundo plano, asegurando que tus datos lleguen al servidor sin impactar el rendimiento.
Este artículo demuestra cómo implementar navigator.sendBeacon()
para la transmisión confiable de datos en segundo plano. Aprenderás aplicaciones prácticas como el seguimiento de analíticas y el reporte de errores, entenderás por qué supera a fetch()
durante las transiciones de página, y verás una implementación completa usando JavaScript moderno.
Puntos Clave
- La API Beacon pone en cola las solicitudes en segundo plano sin bloquear la navegación de páginas
- Usa
navigator.sendBeacon()
para analíticas, reporte de errores y seguimiento de acciones del usuario - Los beacons son más confiables que
fetch()
durante eventos de descarga de página - Mantén las cargas útiles pequeñas y agrupa múltiples eventos para un rendimiento óptimo
- Implementa alternativas para navegadores antiguos que no soportan la API Beacon
- La API devuelve un booleano indicando si se puso en cola exitosamente, no confirmación de entrega
Qué Hace Diferente a la API Beacon
El enfoque de la API Beacon resuelve un problema fundamental: las solicitudes HTTP tradicionales bloquean la navegación de páginas. Cuando usas fetch()
o XMLHttpRequest
durante eventos de descarga, el navegador debe esperar a que la solicitud se complete antes de permitir que la navegación proceda. Esto crea una mala experiencia de usuario y transmisión de datos poco confiable.
La API Beacon opera de manera diferente. Pone en cola las solicitudes de forma asíncrona y maneja la transmisión en segundo plano, incluso después de que la página se haya cerrado. El navegador gestiona todo el proceso sin bloquear la navegación o requerir que tu JavaScript permanezca activo.
Por Qué Fallan los Métodos Tradicionales Durante las Transiciones de Página
Considera este escenario común usando fetch()
:
window.addEventListener('beforeunload', () => {
// Esto puede fallar o bloquear la navegación
fetch('/analytics', {
method: 'POST',
body: JSON.stringify({ event: 'page_exit' })
})
})
Este enfoque tiene varios problemas:
- La solicitud puede ser cancelada cuando la página se descarga
- La navegación se bloquea hasta que la solicitud se complete
- No hay garantía de que los datos lleguen al servidor
- Mala experiencia de usuario con transiciones de página retrasadas
Cómo Funciona navigator.sendBeacon()
El método navigator.sendBeacon()
acepta dos parámetros: un endpoint URL y datos opcionales. Devuelve un booleano indicando si la solicitud fue puesta en cola exitosamente (no si llegó al servidor).
const success = navigator.sendBeacon(url, data)
El navegador maneja la transmisión real, optimizando para las condiciones de red y recursos del sistema. Este enfoque de “disparar y olvidar” es perfecto para analíticas, logging y datos de diagnóstico donde no necesitas una respuesta.
Ejemplos de Implementación Práctica
Seguimiento Básico de Analíticas
Aquí hay una implementación mínima para rastrear sesiones de usuario:
// Rastrear datos de sesión
const sessionData = {
userId: 'user123',
sessionDuration: Date.now() - sessionStart,
pageViews: pageViewCount,
timestamp: new Date().toISOString()
}
// Enviar datos cuando la página se oculta o el usuario navega
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
const payload = JSON.stringify(sessionData)
navigator.sendBeacon('/log', payload)
}
})
window.addEventListener('beforeunload', () => {
const payload = JSON.stringify(sessionData)
navigator.sendBeacon('/log', payload)
})
Sistema de Reporte de Errores
La API Beacon sobresale en capturar y reportar errores de JavaScript:
window.addEventListener('error', (event) => {
const errorData = {
message: event.message,
filename: event.filename,
line: event.lineno,
column: event.colno,
stack: event.error?.stack,
userAgent: navigator.userAgent,
timestamp: Date.now()
}
if (navigator.sendBeacon) {
navigator.sendBeacon('/log', JSON.stringify(errorData))
}
})
Seguimiento de Acciones del Usuario
Rastrea interacciones específicas del usuario sin bloquear la interfaz:
function trackUserAction(action, details) {
const actionData = {
action,
details,
timestamp: Date.now(),
url: window.location.href
}
if (navigator.sendBeacon) {
navigator.sendBeacon('/log', JSON.stringify(actionData))
}
}
// Ejemplos de uso
document.getElementById('cta-button').addEventListener('click', () => {
trackUserAction('cta_click', { buttonId: 'cta-button' })
})
document.addEventListener('scroll', throttle(() => {
const scrollPercent = Math.round(
(window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100
)
trackUserAction('scroll', { percentage: scrollPercent })
}, 1000))
Implementación Completa de Analíticas
Aquí hay un ejemplo integral que combina múltiples escenarios de seguimiento:
class AnalyticsTracker {
constructor(endpoint = '/log') {
this.endpoint = endpoint
this.sessionStart = Date.now()
this.events = []
this.setupEventListeners()
}
setupEventListeners() {
// Rastrear cambios de visibilidad de página
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
this.sendBatch()
}
})
// Rastrear descarga de página
window.addEventListener('beforeunload', () => {
this.sendBatch()
})
// Rastrear errores
window.addEventListener('error', (event) => {
this.trackEvent('error', {
message: event.message,
filename: event.filename,
line: event.lineno
})
})
}
trackEvent(type, data) {
this.events.push({
type,
data,
timestamp: Date.now()
})
// Enviar lote si tenemos demasiados eventos
if (this.events.length >= 10) {
this.sendBatch()
}
}
sendBatch() {
if (this.events.length === 0) return
const payload = {
sessionId: this.generateSessionId(),
sessionDuration: Date.now() - this.sessionStart,
events: this.events,
url: window.location.href,
userAgent: navigator.userAgent
}
if (navigator.sendBeacon) {
const success = navigator.sendBeacon(
this.endpoint,
JSON.stringify(payload)
)
if (success) {
this.events = [] // Limpiar eventos enviados
}
}
}
generateSessionId() {
return Math.random().toString(36).substring(2, 15)
}
}
// Inicializar rastreador
const tracker = new AnalyticsTracker('/log')
// Rastrear eventos personalizados
tracker.trackEvent('page_view', { page: window.location.pathname })
Soporte de Navegadores y Alternativas
La API Beacon tiene excelente soporte en navegadores modernos. Para navegadores antiguos, implementa una alternativa elegante:
function sendData(url, data) {
if (navigator.sendBeacon) {
return navigator.sendBeacon(url, data)
}
// Alternativa para navegadores antiguos
try {
const xhr = new XMLHttpRequest()
xhr.open('POST', url, false) // Síncrono para eventos de descarga
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send(data)
return true
} catch (error) {
console.warn('Falló al enviar datos:', error)
return false
}
}
Mejores Prácticas para la Implementación de Beacon
Mantén las Cargas Útiles Pequeñas
La API Beacon está diseñada para paquetes de datos pequeños. Limita las cargas útiles a información esencial:
// Bueno: Datos enfocados y esenciales
const essentialData = {
event: 'conversion',
value: 29.99,
timestamp: Date.now()
}
// Evitar: Datos grandes e innecesarios
const bloatedData = {
event: 'conversion',
value: 29.99,
timestamp: Date.now(),
entireDOMState: document.documentElement.outerHTML,
allCookies: document.cookie,
completeUserHistory: getUserHistory()
}
Agrupa Múltiples Eventos
En lugar de enviar beacons individuales para cada evento, agrúpalos juntos:
const eventBatch = []
function addEvent(eventData) {
eventBatch.push(eventData)
// Enviar lote cuando alcance cierto tamaño
if (eventBatch.length >= 5) {
sendBatch()
}
}
function sendBatch() {
if (eventBatch.length > 0) {
navigator.sendBeacon('/log', JSON.stringify(eventBatch))
eventBatch.length = 0 // Limpiar el lote
}
}
Maneja las Fallas de Red con Elegancia
Dado que los beacons no proporcionan retroalimentación de respuesta, implementa validación del lado del cliente:
function validateAndSend(data) {
// Validar estructura de datos
if (!data || typeof data !== 'object') {
console.warn('Datos inválidos para beacon')
return false
}
// Verificar tamaño de carga útil (los navegadores típicamente limitan a 64KB)
const payload = JSON.stringify(data)
if (payload.length > 65536) {
console.warn('Carga útil demasiado grande para beacon')
return false
}
return navigator.sendBeacon('/log', payload)
}
API Beacon vs Fetch Durante las Transiciones de Página
La diferencia clave se vuelve aparente durante eventos de descarga de página:
// API Beacon: No bloqueante, confiable
window.addEventListener('beforeunload', () => {
navigator.sendBeacon('/log', JSON.stringify(data)) // ✅ Confiable
})
// API Fetch: Puede bloquear o fallar
window.addEventListener('beforeunload', () => {
fetch('/log', {
method: 'POST',
body: JSON.stringify(data),
keepalive: true // Ayuda pero no garantiza éxito
}) // ❌ Puede fallar durante navegación
})
La bandera keepalive
en las solicitudes fetch proporciona funcionalidad similar pero con menos confiabilidad que la API Beacon, que fue específicamente diseñada para este caso de uso.
Conclusión
La API Beacon proporciona una solución robusta para la transmisión de datos en segundo plano sin bloquear la navegación de páginas. Al poner en cola las solicitudes de forma asíncrona, asegura la entrega confiable de datos mientras mantiene una experiencia de usuario óptima. Ya sea que estés implementando seguimiento de analíticas, reporte de errores o logging de acciones del usuario, navigator.sendBeacon()
ofrece la confiabilidad y rendimiento que los métodos HTTP tradicionales no pueden igualar durante las transiciones de página.
La naturaleza de “disparar y olvidar” de los beacons los hace ideales para escenarios donde necesitas enviar datos pero no requieres una respuesta. Combinado con estrategias adecuadas de agrupación y optimización de carga útil, la API Beacon se convierte en una herramienta esencial para aplicaciones web modernas que priorizan tanto la recolección de datos como la experiencia del usuario.
Preguntas Frecuentes
La API Beacon es un estándar web de JavaScript que envía pequeñas cantidades de datos a un servidor sin esperar una respuesta. A diferencia de fetch o XMLHttpRequest, las solicitudes beacon son puestas en cola por el navegador y enviadas de forma asíncrona, haciéndolas perfectas para analíticas y logging durante eventos de navegación de página donde las solicitudes tradicionales podrían fallar o bloquear la experiencia del usuario.
No, la API Beacon está diseñada para paquetes de datos pequeños, típicamente limitados a 64KB por la mayoría de navegadores. Si necesitas enviar cantidades más grandes de datos, considera agrupar solicitudes más pequeñas o usar métodos alternativos como la API Fetch con la bandera keepalive para escenarios de tiempo no crítico.
La API Beacon tiene excelente soporte en todos los navegadores modernos incluyendo Chrome, Firefox, Safari y Edge. No funciona en Internet Explorer. Para soporte de navegadores antiguos, implementa una alternativa usando XMLHttpRequest o fetch, aunque estas alternativas pueden no proporcionar la misma confiabilidad durante eventos de descarga de página.
El método navigator.sendBeacon devuelve un booleano indicando si la solicitud fue puesta en cola exitosamente por el navegador, no si llegó al servidor. Dado que los beacons son de disparar y olvidar, no puedes acceder a la respuesta del servidor o saber definitivamente si los datos fueron recibidos. Esto es por diseño para un rendimiento óptimo.
Usa la API Beacon cuando necesites enviar datos durante transiciones de página, eventos de descarga, o cuando la página se oculta, y no necesites una respuesta. Usa fetch para llamadas API regulares donde necesites manejar respuestas, errores, o cuando el tiempo no sea crítico para la navegación de página.