Dos APIs para detectar JavaScript lento
Antes de LoAF, existía Long Tasks API (disponible desde Chrome 58, 2017). Ambas detectan JavaScript que bloquea el main thread, pero con diferentes niveles de detalle.
🕐 Long Tasks API (2017)
Detecta tareas que tardan más de 50ms, pero con información limitada
🚀 Long Animation Frames API (2023)
Detecta frames lentos con contexto completo: qué scripts, qué funciones, cuánto layout thrashing
Comparación lado a lado
Long Tasks API
Long Animation Frames API
📊 Comparación:
Haz clic en el botón para comparar ambas APIs
Long Tasks API
Configuración básica
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Long task detected:');
console.log(' Duration:', entry.duration);
console.log(' Start time:', entry.startTime);
console.log(' Name:', entry.name);
console.log(' Attribution:', entry.attribution);
}
});
observer.observe({ type: 'longtask', buffered: true });
Información disponible
{
name: "self",
entryType: "longtask",
startTime: 1234.5,
duration: 234.2,
attribution: [{
name: "unknown",
entryType: "taskattribution",
containerType: "window",
containerName: "",
containerId: ""
}]
}
⚠️ Limitaciones de Long Tasks API:
- ❌ No muestra qué script causó la tarea
- ❌ No muestra qué función se ejecutó
- ❌ No distingue entre cálculo y layout thrashing
- ❌ Solo attribution genérica (iframe, window)
- ✅ Solo útil para saber "algo fue lento"
Long Animation Frames API
Configuración básica
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Long frame detected:');
console.log(' Duration:', entry.duration);
console.log(' Blocking duration:', entry.blockingDuration);
console.log(' Scripts:', entry.scripts.length);
entry.scripts.forEach(script => {
console.log(' Function:', script.sourceFunctionName);
console.log(' File:', script.sourceURL);
console.log(' Duration:', script.duration);
console.log(' Forced layout:', script.forcedStyleAndLayoutDuration);
});
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
Información disponible
{
name: "long-animation-frame",
entryType: "long-animation-frame",
startTime: 1234.5,
duration: 234.2,
renderStart: 1234.8,
styleAndLayoutStart: 1235.0,
blockingDuration: 184.2,
scripts: [{
invoker: "BUTTON.onclick",
sourceFunctionName: "handleClick",
sourceURL: "http://example.com/app.js",
sourceCharPosition: 1234,
duration: 200.5,
executionStart: 1234.6,
forcedStyleAndLayoutDuration: 120.3,
pauseDuration: 0
}]
}
✅ Ventajas de Long Animation Frames API:
- ✅ Muestra exactamente qué script y función
- ✅ Incluye URL y posición en el archivo
- ✅ Distingue cálculo vs layout thrashing
- ✅ Muestra el "invoker" (qué disparó el script)
- ✅ Información completa para debugging
Comparación directa
| Característica | Long Tasks | Long Animation Frames |
|---|---|---|
| Disponibilidad | Chrome 58+ (2017) | Chrome 116+ (2023) |
| Umbral | > 50ms | > 50ms |
| Script attribution | ❌ No | ✅ Sí (función, URL) |
| Forced layout | ❌ No | ✅ Sí |
| Render timing | ❌ No | ✅ Sí (renderStart) |
| Útil para debugging | 🟡 Limitado | ✅ Excelente |
| Uso recomendado | Detección básica | Debugging y optimización |
¿Cuándo usar cada API?
Usa Long Tasks API cuando:
- Solo necesitas saber "hubo bloqueo del main thread"
- Necesitas compatibilidad con navegadores antiguos
- Quieres métricas agregadas simples (count de long tasks)
Usa Long Animation Frames API cuando:
- Necesitas identificar el script/función responsable
- Quieres diagnosticar layout thrashing
- Estás optimizando INP o responsiveness
- Solo Chrome 116+ es aceptable
💡 Estrategia híbrida:
Usa Long Tasks como fallback para navegadores antiguos, y Long Animation Frames cuando esté disponible para obtener información detallada.
Migración de Long Tasks a LoAF
Código compatible con ambas APIs
// Feature detection
const supportsLoaf = PerformanceObserver.supportedEntryTypes.includes(
'long-animation-frame'
);
if (supportsLoaf) {
// Usar LoAF (preferido)
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Frame lento:', entry.duration);
entry.scripts.forEach(script => {
console.log(' Función:', script.sourceFunctionName);
});
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
} else {
// Fallback a Long Tasks
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Tarea lenta:', entry.duration);
// Información limitada disponible
}
});
observer.observe({ type: 'longtask', buffered: true });
}