Script: async, defer
Scopri come gli attributi async e defer degli script JavaScript influenzano il caricamento delle pagine e migliorano le prestazioni web.
La posizione di un tag <script> e il modo in cui viene caricato influiscono direttamente sulla velocità con cui la pagina diventa utilizzabile. Per impostazione predefinita, gli script possono bloccare il browser durante il rendering; gli attributi async e defer permettono di indicare al browser di scaricare JavaScript in background. Questo articolo spiega come si comportano gli script bloccanti normali, cosa cambiano async e defer, perché gli script di modulo vengono differiti automaticamente e come scegliere l'opzione giusta per ogni script.
Come uno script normale blocca il parsing
Un <script> senza async o defer è bloccante per il rendering. Quando il parser HTML raggiunge il tag, smette di costruire la pagina, recupera lo script (se ha un src), lo esegue fino al completamento, e solo allora riprende il parsing del resto del documento:
<p>...content before script...</p>
<script src="big.js"></script> <!-- parsing pauses here until big.js downloads AND runs -->
<p>...content after script (the user can't see this yet)...</p>Da questo derivano due conseguenze:
- Lo script non può vedere gli elementi DOM che compaiono dopo di esso, perché non sono ancora stati analizzati.
- L'utente si trova davanti a una pagina parzialmente costruita mentre lo script viene scaricato. Su una rete lenta, questo è il classico problema della "pagina bianca".
La soluzione tradizionale era inserire gli script in fondo al <body>. async e defer offrono una soluzione più elegante: mantieni il tag nell'<head> ma impediscigli di bloccare.
Sia
asyncchedeferhanno effetto solo sugli script esterni — richiedono l'attributosrc. Vengono ignorati nei blocchi<script>inline.
Capire l'attributo async
Cos'è l'attributo async?
Quando aggiungi l'attributo async a un tag <script>, il browser viene istruito a scaricare lo script in background senza bloccare il parsing HTML. Non appena il download termina, il browser mette in pausa il parsing, esegue lo script e poi continua. Poiché i tempi di download variano, gli script async vengono eseguiti non appena sono pronti — in ordine imprevedibile, possibilmente prima che il resto del documento sia stato analizzato.
Questo rende async perfetto per script indipendenti che non dipendono dal DOM né l'uno dall'altro: analytics, pubblicità e altri tracker "fire-and-forget".
Esempio di codice: utilizzo di async
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Async Example</title>
</head>
<body>
<h1>Testing Async</h1>
<script async src="https://example.com/async-script.js"></script>
</body>
</html>Vantaggi e compromessi di async
- Non bloccante: il parsing HTML continua mentre lo script viene scaricato.
- Esecuzione immediata: lo script viene eseguito nel momento in cui il download termina — ideale per script indipendenti.
- Nessun ordine garantito: con più script
async, viene eseguito prima quello che finisce di scaricarsi per primo. Non usare maiasyncper script che dipendono l'uno dall'altro. - Può essere eseguito prima che il DOM sia pronto: può essere eseguito prima che il parsing sia terminato, quindi non dare per scontato che gli elementi successivi esistano.
Sfruttare l'attributo defer
Cos'è l'attributo defer?
L'attributo defer scarica anch'esso lo script in background senza bloccare il parsing. La differenza sta in quando viene eseguito: uno script differito viene eseguito solo dopo che il documento HTML è stato completamente analizzato, e appena prima che si attivi l'evento DOMContentLoaded. Gli script differiti mantengono inoltre il loro ordine nel documento — il primo tag defer viene sempre eseguito prima del secondo, indipendentemente da quale finisce di scaricarsi prima.
Questo rende defer la scelta predefinita giusta per il codice applicativo: l'intero DOM è garantito esistere e gli script dipendenti rimangono in ordine.
Esempio di codice: utilizzo di defer
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Defer Example</title>
</head>
<body>
<h1>Testing Defer</h1>
<script defer src="https://example.com/defer-script.js"></script>
</body>
</html>Vantaggi dell'utilizzo di defer
- DOM pronto: l'intero documento HTML viene analizzato prima dell'esecuzione dello script.
- Ordine mantenuto: gli script vengono eseguiti nell'ordine del documento, il che è essenziale per gli script che dipendono l'uno dall'altro.
Gli script di modulo sono differiti per impostazione predefinita
Un <script type="module"> si comporta automaticamente come uno script differito — non è necessario aggiungere defer. Viene recuperato senza bloccare il parsing e viene eseguito dopo che il documento è stato analizzato, nell'ordine corretto. Per far eseguire un modulo non appena è pronto (il comportamento di async), aggiungi async esplicitamente:
<!-- Deferred automatically; runs after parsing, in order -->
<script type="module" src="app.js"></script>
<!-- Opt into async: runs as soon as it (and its imports) are ready -->
<script type="module" async src="tracker.js"></script>Se sei alle prime armi con i moduli, consulta l'introduzione ai moduli.
async vs defer a colpo d'occhio
| Comportamento | async | defer |
|---|---|---|
| Blocca il parsing HTML? | No | No |
| Quando viene eseguito? | Non appena termina il download | Dopo il parsing, prima di DOMContentLoaded |
| Ordine di esecuzione | Prima chi finisce di scaricarsi | Ordine del documento, garantito |
| DOM completamente disponibile? | Non garantito | Sì |
| Ideale per | Script indipendenti (analytics, pubblicità) | Codice applicativo, script dipendenti |
Una regola semplice:
- Usa
asyncquando lo script è autonomo — non dipende da altri script né dal DOM. - Usa
deferquando lo script ha bisogno dell'intero DOM, o quando l'ordine di esecuzione è importante.
Esempio pratico: decisioni sul caricamento degli script
Considera il caso di caricare una libreria di utilità (come Lodash) più il tuo file che ne dipende. Poiché l'ordine è importante, defer è la scelta giusta — entrambi vengono scaricati in background ma vengono eseguiti in sequenza dopo il parsing:
<script defer src="https://cdn.jsdelivr.net/npm/lodash/lodash.min.js"></script>
<script defer src="script.js"></script>Qui lodash.min.js è garantito essere eseguito prima di script.js, e entrambi attendono che la pagina sia analizzata. Passare questi a async rischierebbe che script.js venga eseguito per primo e fallisca perché Lodash non è ancora caricato.
Per caricare risorse non-script (immagini, stili) e reagire al loro successo o fallimento, consulta Caricamento delle risorse: onload e onerror.
Conclusione
Utilizzare efficacemente gli attributi async e defer nei tag script è fondamentale per lo sviluppo web moderno. Comprendendoli e applicandoli correttamente, gli sviluppatori possono garantire caricamenti di pagina più rapidi e una migliore esperienza utente. Il caricamento asincrono degli script riguarda l'ottimizzazione delle prestazioni e la realizzazione di applicazioni web efficienti e centrate sull'utente.