Script sincroni e asincroni in HTML: quando scegliere uno script defer

by Anastasia P.
SHARE
Script sincroni e asincroni in HTML: quando scegliere uno script defer
© Guida-HTML5.it

Introduzione

Quando inseriamo uno script in una pagina HTML, il browser deve decidere quando scaricarlo e quando eseguirlo. Questa scelta influisce direttamente su prestazioni, ordine di caricamento e comportamento della pagina. In molti casi, il vero problema non è solo distinguere tra script sincroni e asincroni, ma capire come evitare che un file JavaScript blocchi il rendering senza perdere il controllo sull’ordine di esecuzione.

In questo tutorial ci concentriamo su un sotto-argomento molto utile e pratico: l’attributo defer. È una soluzione spesso migliore rispetto a uno script sincrono tradizionale e, in diversi casi, anche più adatta di async. Vedremo come funziona, quando usarlo e come combinarlo con HTML moderno in modo pulito e prevedibile.

Codice completo

<!DOCTYPE html>
<html lang="it">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Esempio di script defer</title>

  <!-- 
    defer: il browser scarica lo script in parallelo
    ma lo esegue solo dopo aver completato il parsing dell´HTML.
    L´ordine tra gli script defer viene mantenuto.
  -->
  <script src="menu.js" defer></script>
  <script src="analytics.js" defer></script>
</head>
<body>
  <header>
    <h1>Demo: script defer</h1>
  </header>

  <main>
    <button id="openMenu">Apri menu</button>
    <nav id="mobileMenu" hidden>
      <ul>
        <li>Home</li>
        <li>Servizi</li>
        <li>Contatti</li>
      </ul>
    </nav>
  </main>

  <script>
    // Questo script inline gira subito, quindi deve essere usato con attenzione.
    // Se dipende da elementi già presenti nel DOM, conviene metterlo in fondo al body
    // oppure usare defer in un file esterno.
    console.log("HTML già disponibile per gli script defer.");
  </script>
</body>
</html>

Spiegazione

Il comportamento degli script dipende dal modo in cui il browser costruisce la pagina. Durante il parsing dell’HTML, il browser legge il documento dall’alto verso il basso. Se incontra uno script sincrono classico come <script src="file.js"></script>, in genere si ferma, scarica il file, lo esegue e poi riprende a leggere l’HTML. Questo può rallentare la visualizzazione della pagina, soprattutto se il file JavaScript è pesante o si trova su una rete lenta.

L’attributo defer cambia questo comportamento in modo molto utile:

  • il browser scarica il file in parallelo mentre continua a leggere l’HTML;
  • l’esecuzione avviene dopo il parsing completo del documento;
  • gli script con defer mantengono l’ordine di apparizione nel documento.

Questo significa che defer è perfetto quando lo script deve lavorare sul DOM già costruito, ad esempio per agganciare eventi, inizializzare menu, slider, validazioni o componenti interattivi.

Confronto pratico tra sincrono, async e defer

  • Sincrono: blocca il parsing finché il file non è scaricato ed eseguito. Utile solo in casi particolari.
  • Async: scarica in parallelo e esegue appena pronto, ma senza garantire l’ordine. Ideale per script indipendenti come analytics o widget esterni.
  • Defer: scarica in parallelo, esegue dopo il parsing, mantenendo l’ordine. È spesso la scelta migliore per gli script dell’applicazione.

Esempio reale: menu mobile

Immagina di avere un pulsante che apre un menu mobile. Se lo script viene eseguito troppo presto, il browser potrebbe non aver ancora creato il pulsante nel DOM. In quel caso, document.getElementById("openMenu") restituirebbe null e il codice fallirebbe.

Con defer, invece, puoi scrivere il file JavaScript sapendo che il DOM sarà già disponibile al momento dell’esecuzione:

// menu.js
const button = document.getElementById("openMenu");
const menu = document.getElementById("mobileMenu");

if (button && menu) {
  button.addEventListener("click", () => {
    const isHidden = menu.hasAttribute("hidden");
    if (isHidden) {
      menu.removeAttribute("hidden");
      button.textContent = "Chiudi menu";
    } else {
      menu.setAttribute("hidden", "");
      button.textContent = "Apri menu";
    }
  });
}

Questo codice è semplice, leggibile e non richiede trucchi come DOMContentLoaded se il file è caricato con defer.

Perché defer è spesso la scelta più equilibrata

Molti sviluppatori usano async pensando che sia sempre la soluzione più veloce. In realtà, la velocità non è l’unico criterio. Se hai due script che dipendono l’uno dall’altro, ad esempio utils.js e app.js, con async potresti avere problemi perché l’ordine di esecuzione non è garantito. Con defer, invece, l’ordine resta stabile:

<script src="utils.js" defer></script>
<script src="app.js" defer></script>

In questo caso utils.js verrà eseguito prima di app.js, anche se il download termina in momenti diversi. Questo è molto importante nei progetti reali, dove un file può definire funzioni, classi o configurazioni usate da un altro file.

Best practice

  • Usa defer per gli script dell’applicazione: è la scelta più affidabile quando il codice interagisce con il DOM.
  • Usa async solo per script indipendenti: ad esempio tracker, analytics, chat widget o contenuti che non dipendono dal resto della pagina.
  • Evita script sincroni nel <head> se non sono strettamente necessari: possono rallentare il rendering iniziale.
  • Mantieni l’ordine dei file quando uno script dipende da un altro: con defer l’ordine è rispettato, con async no.
  • Non mescolare logiche di inizializzazione fragili: se il tuo codice richiede elementi DOM già presenti, preferisci file esterni con defer o inserisci lo script in fondo al body.
  • Verifica la compatibilità dei componenti di terze parti: alcuni script esterni sono pensati per essere caricati con async, altri richiedono un ordine preciso.
  • Organizza il codice in moduli piccoli: più il file è chiaro, più sarà facile decidere se usare sincrono, asincrono o defer.

Una buona regola pratica è questa: se lo script serve alla pagina e dipende dal DOM, usa defer. Se invece lo script è autonomo e non influenza la struttura della pagina, allora puoi valutare async.

Riepilogo

La differenza tra script sincroni e asincroni non riguarda solo la velocità, ma anche il modo in cui il browser gestisce il caricamento della pagina. Lo script sincrono blocca il parsing, async esegue appena pronto senza ordine garantito, mentre defer carica in parallelo e rimanda l’esecuzione fino a quando l’HTML è stato completamente analizzato.

Per la maggior parte dei progetti web moderni, defer è la soluzione più equilibrata per gli script principali dell’applicazione. Ti permette di evitare blocchi, mantenere l’ordine e lavorare sul DOM in modo sicuro. È una scelta semplice, pratica e molto efficace per migliorare sia la struttura del codice sia l’esperienza utente.

Approfondisci con risorse ufficiali

  • MDN Web Docs — Documentazione su <script>, async e defer
  • WHATWG HTML Standard — Specifica ufficiale del comportamento degli script in HTML
  • Web.dev — Articoli sulle prestazioni del caricamento JavaScript
  • Google Chrome Developers — Guide pratiche su rendering e performance

SHARE