14 métodos para diferir o programar JavaScript
Aprende cómo diferir y programar JavaScript

¿Por qué diferir o programar JavaScript?
JavaScript puede (y va a) ralentizar tu sitio web de varias maneras. Esto puede tener todo tipo de impacto negativo en los Core Web Vitals. JavaScript puede competir por recursos de red, puede competir por recursos de CPU (bloquear el hilo principal) e incluso puede bloquear el análisis de una página web. Diferir y programar tus scripts puede mejorar drásticamente los Core Web Vitals.
Table of Contents!
- ¿Por qué diferir o programar JavaScript?
- ¿Cómo puede la temporización de JavaScript afectar los Core Web Vitals?
- ¿Cómo elegir el método de diferimiento correcto?
- Método 1: Usar el atributo defer
- Método 2: Usar el atributo async
- Método 3: Usar módulos
- Método 4: Colocar scripts cerca del final de la página
- Método 5: Inyectar scripts
- Método 6: Inyectar scripts en un momento posterior
- Método 7: Cambiar el tipo de script (y luego cambiarlo de vuelta)
- Método 9: Usar readystatechange
- Método 10: Usar setTimeout sin timeout
- Método 11: Usar setTimeout con un timeout
- Método 12: Usar una promesa para establecer una microtarea
- Método 13: Usar una microtarea
- Método 15: Usar postTask
Para minimizar los efectos negativos que JavaScript puede tener en los Core Web Vitals, generalmente es una buena idea especificar cuándo un script se pone en cola para descarga y programar cuándo puede consumir tiempo de CPU y bloquear el hilo principal.
¿Cómo puede la temporización de JavaScript afectar los Core Web Vitals?
¿Cómo puede la temporización de JavaScript afectar los Core Web Vitals? Solo observa este ejemplo de la vida real. La primera página se carga con JavaScript que 'bloquea el renderizado'. Las métricas de pintado así como el Time to interactive son bastante malas. El segundo ejemplo es de exactamente la misma página pero con el JavaScript diferido. Verás que la imagen LCP aún sufrió un gran impacto. El tercer ejemplo tiene el mismo script ejecutado después del evento 'load' de la página y tiene las llamadas a funciones divididas en piezas más pequeñas. Este último pasa los Core Web Vitals con margen de sobra.



Por defecto, el JavaScript externo en el head de la página bloqueará la creación del árbol de renderizado. Más específicamente: cuando el navegador encuentra un script en el documento debe pausar la construcción del DOM, ceder el control al motor de JavaScript, y dejar que el script se ejecute antes de continuar con la construcción del DOM. Esto afectará tus métricas de pintado (Largest Contentful Paint y First Contentful Paint).
El JavaScript diferido o asíncrono aún puede impactar las métricas de pintado, especialmente el Largest Contentful Paint porque se ejecutará y bloqueará el hilo principal, una vez que el DOM haya sido creado (y elementos comunes de LCP como imágenes podrían no haberse descargado aún).
Los archivos JavaScript externos también competirán por recursos de red. Los archivos JavaScript externos generalmente se descargan antes que las imágenes. SI estás descargando demasiados scripts, la descarga de tus imágenes se retrasará.
Por último pero no menos importante, JavaScript podría bloquear o retrasar la interacción del usuario. Cuando un script está usando recursos de CPU (bloqueando el hilo principal) un navegador no responderá a la entrada (clics, desplazamiento, etc.) hasta que ese script haya terminado.
¿Cómo soluciona la programación o el diferimiento de JavaScript los Core Web Vitals?
¿Cómo elegir el método de diferimiento correcto?
No todos los scripts son iguales y cada script tiene su propia funcionalidad. Algunos scripts son importantes tenerlos temprano en el proceso de renderizado, otros no.

Me gusta categorizar los JavaScripts en 4 grupos según su nivel de importancia.
1. Crítico para el renderizado. Estos son los scripts que cambiarán la apariencia de una página web. Si no se cargan, la página no se verá completa. Estos scripts deben evitarse a toda costa. Si no puedes evitarlos por alguna razón, no deberían diferirse. Por ejemplo, un slider superior o un script de pruebas A/B.
2. Crítico. Estos scripts no cambiarán la apariencia de una página web (demasiado) pero la página no funcionará bien sin ellos. Estos scripts deberían diferirse o cargarse de forma asíncrona. Por ejemplo, los scripts de tu menú.
3. Importante. Estos son scripts que quieres cargar porque son valiosos para ti o para el visitante. Yo tiendo a cargar estos scripts después de que el evento load se haya disparado. Por ejemplo, analytics o un botón de 'volver arriba'.
4. Opcional. Estos son scripts sin los que puedes vivir si es absolutamente necesario. Yo cargo estos scripts con la prioridad más baja y solo los ejecuto cuando el navegador está inactivo. Por ejemplo, un widget de chat o un botón de Facebook.
Método 1: Usar el atributo defer
Los scripts con el atributo defer se descargarán en paralelo y se añaden a la cola de JavaScript diferido. Justo antes de que el navegador dispare el evento DOMContentLoaded, todos los scripts en esa cola se ejecutarán en el orden en que aparecen en el documento.
<script src='javascript.js'></script> El 'truco del defer' generalmente soluciona muchos problemas, especialmente las métricas de pintado. Desafortunadamente no hay garantía, depende de la calidad de los scripts. Los scripts diferidos se ejecutarán una vez que todos los scripts se hayan cargado y el HTML esté analizado (DOMContentLoaded). El elemento LCP (generalmente una imagen grande) podría no haberse cargado para entonces y los scripts diferidos aún causarán un retraso en el LCP.
Cuándo usar:
Usa scripts diferidos para scripts críticos que se necesitan lo antes posible.
Ventajas:
- Los scripts diferidos se descargarán en paralelo
- El DOM estará disponible en el momento de la ejecución
Desventajas:
- Los scripts diferidos podrían retrasar tus métricas de LCP
- Los scripts diferidos bloquearán el hilo principal una vez que se ejecuten
- Podría no ser seguro diferir scripts cuando scripts inline o async dependen de ellos
Método 2: Usar el atributo async
Los scripts con el atributo async se descargan en paralelo y se ejecutarán inmediatamente después de que hayan terminado de descargarse.
<script src='javascript.js'></script> Los scripts async harán poco para solucionar tus problemas de velocidad de página. Es genial que se descarguen en paralelo pero eso es todo. Una vez que se descargan, bloquearán el hilo principal mientras se ejecutan.
Cuándo usar:
Usa scripts async para scripts críticos que se necesitan lo antes posible y son autocontenidos (no dependen de otros scripts).
Ventajas:
- Los scripts async se descargarán en paralelo.
- Los scripts async se ejecutarán lo antes posible.
Desventajas:
- DOMContentLoaded puede ocurrir tanto antes como después de async.
- El orden de ejecución de los scripts será desconocido de antemano.
- No puedes usar scripts async que dependan de otros scripts async o diferidos
Método 3: Usar módulos
Los scripts modulares se difieren por defecto a menos que tengan el atributo async. En ese caso se tratarán como scripts async
<script src='javascript.js'></script> Los módulos son una nueva forma de pensar sobre JavaScript y solucionan algunos defectos de diseño. Aparte de eso, usar módulos de script no acelerará tu sitio web.
Cuándo usar:
Cuando tu aplicación está construida de forma modular, tiene sentido usar también módulos de JavaScript.
Ventajas:
- Los módulos se difieren por defecto
- Los módulos son más fáciles de mantener y funcionan muy bien con el diseño web modular
- Los módulos permiten una fácil división de código con importaciones dinámicas donde solo importas los módulos que necesitas en un momento determinado.
Desventajas:
- Los módulos por sí solos no mejorarán los Core Web Vitals
- Importar módulos justo a tiempo o sobre la marcha podría ser lento y empeorar el FID e INP
Método 4: Colocar scripts cerca del final de la página
Los scripts en el footer se ponen en cola para descarga en un momento posterior. Esto priorizará otros recursos que están en el documento por encima de la etiqueta script.
<html>
<head></head>
<body>
[el contenido de tu página aquí]
<script defer src='javascript.js'></script>
</body>
</html> Colocar tus scripts al final de la página es una técnica interesante. Esto programará otros recursos (como imágenes) antes que tus scripts. Esto aumentará la probabilidad de que estén disponibles para el navegador y pintados en la pantalla antes de que los archivos JavaScript hayan terminado de descargarse y el hilo principal sea bloqueado por la ejecución del script. Aun así... sin garantía.
Cuándo usar:
Cuando tus scripts ya funcionan bastante bien pero quieres priorizar ligeramente otros recursos como imágenes y fuentes web.
Ventajas:
- Colocar scripts al final de la página no requiere mucho conocimiento.
- Si se hace correctamente no hay riesgo de que esto rompa tu página
Desventajas:
- Los scripts críticos podrían descargarse y ejecutarse más tarde
- No soluciona ningún problema subyacente de JavaScript
Método 5: Inyectar scripts
Los scripts inyectados se tratan como scripts async. Se descargan en paralelo y se ejecutan inmediatamente después de descargarse.
<script>
const loadScript = (scriptSource) => {
const script = document.createElement('script');
script.src = scriptSource;
document.head.appendChild(script);
}
// call the loadscript function that injects 'javascript.js'
loadScript('javascript.js');
</script> Desde la perspectiva de los Core Web Vitals, esta técnica es exactamente igual que usar <script async>.
Cuándo usar:
Este método es frecuentemente usado por scripts de terceros que se disparan lo antes posible. La llamada a función facilita encapsular y comprimir código.
Ventajas:
- Código contenido que inyecta un script async.
Desventajas:
- DOMContentLoaded puede ocurrir tanto antes como después de que el script se haya cargado.
- El orden de ejecución de los scripts será desconocido de antemano.
- No puedes usar esto en scripts que dependan de otros scripts async o diferidos
Método 6: Inyectar scripts en un momento posterior
Los scripts opcionales en mi opinión nunca deberían cargarse diferidos. Deberían inyectarse en el momento más oportuno. En el ejemplo a continuación, el script se ejecutará después de que el navegador haya enviado el evento 'load'.
<script>
window.addEventListener('load', function () {
// see method 5 for the loadscript function
loadScript('javascript.js');
});
</script> Esta es la primera técnica que mejorará de forma fiable el Largest Contentful Paint. Todos los recursos importantes , incluyendo las imágenes, se habrán descargado cuando el navegador dispare el 'evento load'. Esto podría introducir todo tipo de problemas porque podría tardar mucho tiempo en llamarse el evento load.
Cuándo usar:
Para scripts opcionales que no tienen razón para impactar las métricas de pintado.
Ventajas:
- No competirá por recursos críticos porque inyectará el script una vez que la página y sus recursos se hayan cargado
Desventajas:
- Si tu página está mal diseñada en términos de Core Web Vitals, podría tardar mucho tiempo en que la página envíe el evento 'load'
- Debes tener cuidado de no aplicar esto a scripts críticos (como lazy loading, menú, etc.)
Método 7: Cambiar el tipo de script (y luego cambiarlo de vuelta)
Si se encuentra una etiqueta script en algún lugar de la página que 1. tiene un atributo type y 2. el atributo type no es "text/javascript", el script no será descargado ni ejecutado por el navegador. Muchos cargadores de JavaScript (como RocketLoader de CloudFlare) se basan en este principio. La idea es bastante simple y elegante.
Primero, todos los scripts se reescriben así:
<script src="javascript.js"></script> Luego, en algún momento durante el proceso de carga, estos scripts se convierten de vuelta a 'javascripts normales'.
Cuándo usar:
Este no es un método que recomendaría. Solucionar el impacto de JavaScript requerirá mucho más que simplemente mover cada script un poco más abajo en la cola. Por otro lado, si tienes poco control sobre los scripts que se ejecutan en la página o tienes conocimientos insuficientes de JavaScript, esta podría ser tu mejor opción.
Ventajas:
- Es fácil, solo habilita rocket loader u otro plugin y todos tus scripts ahora se ejecutan en un momento algo posterior.
- Probablemente solucionará tus métricas de pintado siempre que no uses lazy loading basado en JS.
- Funcionará para scripts inline y externos.
Desventajas:
- No tendrás control detallado sobre cuándo se ejecutan los scripts
- Scripts mal escritos podrían romperse
- Usa JavaScript para solucionar JavaScript
- No hace nada para solucionar scripts de larga ejecución
Método 8: Usar el intersection observer
Con el intersection observer puedes ejecutar una función (que en este caso carga un JavaScript externo) cuando un elemento se desplaza hacia el viewport visible.
<script>
const handleIntersection = (entries, observer) => {
if (entries?.[0].isIntersecting) {
// load your script or execute another
function like trigger a lazy loaded element
loadScript('javascript.js');
// remove the observer
observer.unobserve(entries?.[0].target);
}
};
const Observer = new window.IntersectionObserver()
Observer.observe(document.querySelector('footer'));
</script> Este es por lejos el método más efectivo para diferir JavaScript que existe. Solo carga los scripts que necesitas, justo antes de necesitarlos. Desafortunadamente, la vida real casi nunca es tan limpia y no muchos scripts pueden vincularse a un elemento que se desplaza hacia la vista.
Cuándo usar:
¡Usa esta técnica tanto como sea posible! Siempre que un script solo interactúe con un componente fuera de pantalla (como un mapa, un slider, un formulario) esta es la mejor manera de inyectar este script.
Ventajas:
- No interferirá con los Core Web Vitals LCP y FCP
- Nunca inyectará scripts que no se usen. Esto mejorará el FID e INP
Desventajas:
- No debería usarse con componentes que podrían estar en el viewport visible
- Es más difícil de mantener que un básico <script src="...">
- Podría introducir un layout shift
Método 9: Usar readystatechange
document.readystate puede usarse como alternativa a los eventos 'DOMContentloaded' y 'load'. El readystate 'interactive' generalmente es un buen lugar para llamar scripts críticos que necesitan cambiar el DOM o agregar manejadores de eventos.
El readystate 'complete' es un buen lugar para llamar scripts que son menos críticos.
document.addEventListener('readystatechange', (event) => {
if (event.target.readyState === 'interactive') {
initLoader();
} else if (event.target.readyState === 'complete') {
initApp();
}
}); Método 10: Usar setTimeout sin timeout
setTimeout es un método mal visto pero muy subestimado en la comunidad de velocidad de página. setTimeout ha recibido una mala reputación porque a menudo se usa mal. Muchos desarrolladores creen que setTimeout solo puede usarse para retrasar la ejecución de scripts por la cantidad establecida de milisegundos. Aunque esto es cierto, setTimeout en realidad hace algo mucho más interesante. Crea una nueva tarea al final del bucle de eventos del navegador. Este comportamiento puede usarse para programar tus tareas de manera efectiva. También puede usarse para dividir tareas largas en tareas más pequeñas separadas
<script>
setTimeout(() => {
// load a script or execute another function
console.log('- I am called from a 0ms timeOut()')
}, 0);
console.log('- I was last in line but executed first')
/*
Output:
- I was last in line but executed first
- I am called from a 0ms timeOut()
*/
</script> Cuándo usar:
setTimeout crea una nueva tarea en el bucle de eventos del navegador. Usa esto cuando tu hilo principal esté siendo bloqueado por muchas llamadas a funciones que se ejecutan secuencialmente.
Ventajas:
- Puede dividir código de larga ejecución en piezas más pequeñas.
Desventajas:
- setTimeout es un método bastante crudo y no ofrece priorización para scripts importantes.
- Añadirá el código a ejecutar al final del bucle
Método 11: Usar setTimeout con un timeout
Las cosas se ponen aún más interesantes cuando llamamos a setTimeout con un timeout de más de 0ms
<script>
setTimeout(() => {
// load a script or execute another function
console.log('- I am called from a 10ms timeOut()')
}, 10);
setTimeout(() => {
// load a script or execute another function
console.log('- I am called from a 0ms timeOut()')
}, 0);
console.log('- I was last in line but executed first')
/*
Output:
- I was last in line but executed first
- I am called from a 0ms timeOut()
- I am called from a 10ms timeOut()
*/
</script> Cuándo usar:
Cuando necesitas un método fácil para programar un script después de otro, un pequeño timeout hará el trabajo
Ventajas:
- Compatible con todos los navegadores
Desventajas:
- No ofrece programación avanzada
Método 12: Usar una promesa para establecer una microtarea
Crear una microtarea también es una forma interesante de programar JavaScript. Las microtareas se programan para ejecutarse inmediatamente después de que el bucle de ejecución actual haya terminado.
<script>
const myPromise = new Promise((resolve, reject) => {
resolve();
}).then(
() => {
console.log('- I was scheduled after a promise')
}
);
console.log('- I was last in line but executed first')
/*
Output:
- I was last in line but executed first
- I was scheduled after a promise
*/
</script> Cuándo usar:
Cuando una tarea necesita programarse inmediatamente después de otra tarea.
Ventajas:
- La microtarea se programará inmediatamente después de que la tarea haya terminado de ejecutarse.
- Una microtarea puede usarse para retrasar piezas de código JavaScript menos importantes en el mismo evento.
Desventajas:
- No dividirá el hilo principal en partes más pequeñas. El navegador no tendrá oportunidad de responder a la entrada del usuario.
- Probablemente nunca necesitarás usar microtareas para mejorar los Core Web Vitals a menos que ya sepas exactamente lo que estás haciendo.
Método 13: Usar una microtarea
El mismo resultado puede lograrse usando queueMicrotask(). La ventaja de usar queueMicrotask() sobre una promesa es que es ligeramente más rápido y no necesitas manejar tus promesas.
<script>
queueMicrotask(() => {
console.log('- I am a microtask')
})
console.log('- I was last in line but executed first')
/*
Output:
- I was last in line but executed first
- I am a microtask
*/
</script> Método 14: Usar requestIdleCallback
El método window.requestIdleCallback() pone en cola una función para ser llamada durante los períodos de inactividad del navegador. Esto permite a los desarrolladores realizar trabajo de fondo y de baja prioridad en el bucle de eventos principal, sin impactar eventos críticos de latencia como animación y respuesta de entrada. Las funciones se llaman generalmente en orden de primero en entrar, primero en salir; sin embargo, los callbacks que tienen un timeout especificado pueden llamarse fuera de orden si es necesario para ejecutarlos antes de que el timeout expire.
<script>
requestIdleCallback(() => {
const script = document.createElement('script');
script.src = 'javascript.js';
document.head.appendChild(script);
});
</script> Cuándo usar:
Usa esto para scripts opcionales o para manejar tareas no críticas después de la entrada del usuario
Ventajas:
- Ejecutar JavaScript con mínimo impacto para el usuario
- Muy probablemente mejorará el FID e INP
Desventajas:
- Compatible con la mayoría de navegadores pero no todos. Existen polyfills que recurren a setTimeout();
- No hay garantía de que el código se ejecute alguna vez
Método 15: Usar postTask
El método permite a los usuarios especificar opcionalmente un retraso mínimo antes de que la tarea se ejecute, una prioridad para la tarea, y una señal que puede usarse para modificar la prioridad de la tarea y/o abortarla. Devuelve una promesa que se resuelve con el resultado de la función callback de la tarea, o se rechaza con la razón de aborto o un error lanzado en la tarea.
<script>
scheduler.postTask(() => {
const script = document.createElement('script');
script.src = 'javascript.js';
document.head.appendChild(script);
}, { priority: 'background' });
</script> Cuándo usar:
postTask es la API perfecta para programar scripts. Desafortunadamente, el soporte de navegadores en este momento es malo, así que no deberías usarlo.
Ventajas:
- ¡Control completo sobre la programación de ejecución de JavaScript!
Desventajas:
- No es compatible con algunos navegadores importantes.
Performance is a Feature.
Treating speed as an afterthought fails. Build a performance culture with a dedicated 2-sprint optimization overhaul.
- 2-Sprint Overhaul
- Culture Building
- Sustainable Speed

