- En JavaScript,
setTimeout(0)en realidad no se ejecuta de inmediato, y muchas veces se retrasa al menos 4 ms; esta es una limitación predeterminada del navegador para evitar abusos - Estas restricciones buscan impedir que los sitios web abusen indiscriminadamente de los temporizadores y provoquen consumo de batería o degradación de la interacción; en modo de batería puede limitarse a 16 ms y en pestañas en segundo plano incluso a 1 segundo
- Para sortear las limitaciones de
setTimeout, los desarrolladores han usado diversas APIs alternativas de temporización comosetImmediate,MessageChannel.postMessage,window.postMessageyscheduler.postTask - Según los benchmarks reales, Chrome y Firefox aplican el límite de 4 ms, pero
MessageChannelyscheduler.postTaskfuncionan casi sin demora, mientras que Safari se caracteriza por restringirsetTimeoutde forma más agresiva - En el fondo, se trata de un equilibrio entre proteger la experiencia del usuario y la libertad del desarrollador; hoy la Scheduler API se está consolidando como la solución estandarizada, aunque si hay abusos también podrían introducirse nuevas intervenciones del navegador (Intervention)
Contexto de la limitación de setTimeout
- Incluso con
setTimeout(0), por el abuso muchas veces la ejecución real ocurre al menos 4 ms despuésconst start = performance.now() setTimeout(() => { // se ejecuta después de unos 4 ms console.log(performance.now() - start) }, 0) - Esto busca frenar las llamadas repetitivas indiscriminadas para reducir el consumo de batería y los retrasos de renderizado
- Algunos navegadores endurecen la restricción según el entorno
- Modo batería: 16 ms en Edge antiguo
- Pestaña en segundo plano: hasta 1 segundo de retraso en Chrome
Aparición de otras APIs de temporización
setImmediate: solo compatible con IE y Edge antiguo; en la práctica está obsoletaMessageChannel.postMessage: envía tareas al event loop mediante un canal separadowindow.postMessage: tiene buen rendimiento, pero existe riesgo de conflicto con otros scriptsscheduler.postTask: compatible con navegadores modernos y considerado la opción más estable
Resultados del benchmark (MacBook Pro 2021, 101 iteraciones)
- Chrome 139:
setTimeout4.2 ms,scheduler.postTask0 ms - Firefox 142:
setTimeout4.72 ms,scheduler.postTask0.01 ms - Safari 18.4:
setTimeout26.73 ms,MessageChannel0.52 ms,window.postMessage0.05 ms
Caso de fake-indexeddb
- IndexedDB quiere hacer commit automático de las transacciones justo después de que terminen las microtareas del event loop
- En Node.js,
setImmediatees ideal, pero en el navegadorsetTimeoutresulta ineficiente - En Chrome, una tarea que toma 300 ms en Node puede alargarse hasta 4.8 segundos en el navegador
- Como solución, se usa
scheduler.postTaskpor defecto y se adoptanMessageChannel/window.postMessagecomo fallback por compatibilidad
Debate sobre la intervención del navegador
- Un lado sostiene que hay que limitar los temporizadores para que los desarrolladores estén protegidos de sí mismos
- El otro lado sostiene que debe garantizarse la libertad para que los desarrolladores puedan medir y optimizar por su cuenta
- Al final, siguiendo el principio de priorizar al usuario, el navegador interviene para evitar abusos
- La Scheduler API busca conciliar ambas posturas, dando a los desarrolladores control detallado sobre las tareas mientras se alinea con el pipeline de renderizado del navegador
Perspectivas a futuro
- Parece que
postTaskypostMessageseguirán sin throttling por un tiempo - Pero si se abusan prioridades altas como
user-blocking, podría volver a haber intervención - A largo plazo, quizá haga falta otra API alternativa como
scheduler2
Aún no hay comentarios.