Introduzione
Quando una pagina HTML include uno script esterno, il browser deve scaricarlo, analizzarlo ed eseguirlo. Il modo in cui questa operazione avviene può influenzare il caricamento della pagina, l’interazione dell’utente e la corretta raccolta dei dati.
Gli script sincroni, inseriti normalmente con il tag <script src="...">, bloccano l’analisi dell’HTML fino al completamento del download e dell’esecuzione. Gli script asincroni, invece, usano l’attributo async: il download procede parallelamente all’analisi del documento e lo script viene eseguito appena è disponibile.
Un caso pratico molto comune riguarda gli strumenti di Analytics, i pixel pubblicitari, i widget di chat e altri servizi di terze parti. Questi strumenti non dovrebbero rallentare la pagina né raccogliere dati prima che l’utente abbia espresso il consenso, quando tale consenso è richiesto. Per questo è importante combinare correttamente caricamento asincrono, gestione del consenso ed eventi della pagina.
Codice completo
<!-- Area iniziale per mostrare lo stato del sistema -->
<p id="analytics-status" role="status">
Analytics non ancora attivato.
</p>
<button id="accept-analytics" type="button">
Accetta Analytics
</button>
<button id="reject-analytics" type="button">
Rifiuta Analytics
</button>
<script>
// Recupera gli elementi necessari dalla pagina.
const statusElement = document.querySelector("#analytics-status");
const acceptButton = document.querySelector("#accept-analytics");
const rejectButton = document.querySelector("#reject-analytics");
// Evita di caricare nuovamente lo stesso script.
let analyticsPromise = null;
function updateStatus(message) {
statusElement.textContent = message;
}
function loadAnalytics() {
// Se lo script è già in fase di caricamento o è stato caricato,
// restituisce la stessa Promise.
if (analyticsPromise) {
return analyticsPromise;
}
analyticsPromise = new Promise((resolve, reject) => {
const script = document.createElement("script");
// Il download non blocca l´analisi del documento.
script.async = true;
script.src = "https://example.com/analytics.js";
script.onload = () => {
resolve();
};
script.onerror = () => {
reject(new Error("Impossibile caricare Analytics."));
};
document.head.appendChild(script);
});
return analyticsPromise;
}
async function enableAnalytics() {
try {
updateStatus("Caricamento di Analytics...");
await loadAnalytics();
// Questo evento viene inviato solo dopo il caricamento riuscito.
if (typeof window.analyticsTrack === "function") {
window.analyticsTrack("consent_granted");
}
updateStatus("Analytics attivato.");
acceptButton.disabled = true;
rejectButton.disabled = true;
} catch (error) {
console.error(error);
updateStatus("Analytics non disponibile.");
}
}
function rejectAnalytics() {
// Non viene creato alcun tag script e quindi non parte alcuna richiesta.
updateStatus("Analytics rifiutato.");
acceptButton.disabled = true;
rejectButton.disabled = true;
}
acceptButton.addEventListener("click", enableAnalytics);
rejectButton.addEventListener("click", rejectAnalytics);
</script> Spiegazione
Perché non caricare subito lo script
Caricare automaticamente un servizio esterno all’apertura della pagina può produrre richieste di rete prima che l’utente abbia scelto se accettare o rifiutare il trattamento dei dati. Inoltre, ogni risorsa esterna può aggiungere latenza, dipendenze DNS e codice JavaScript da elaborare.
Nell’esempio, il tag <script> non è presente direttamente nel markup iniziale. Viene creato dinamicamente solo quando l’utente preme il pulsante di accettazione. In questo modo il caricamento è ritardato fino a quando diventa realmente necessario.
Il significato di async
La proprietà script.async = true indica che il browser può scaricare lo script senza bloccare l’elaborazione del resto della pagina. Tuttavia, quando il download termina, il codice può essere eseguito immediatamente. Per questo uno script asincrono non è adatto quando deve necessariamente precedere altri script.
Nel nostro caso questa caratteristica è utile: Analytics non deve bloccare l’interfaccia e non ha bisogno di essere eseguito prima che l’utente interagisca con la pagina.
Perché usare una Promise
La variabile analyticsPromise memorizza l’operazione di caricamento. Se più parti dell’applicazione chiedessero di attivare Analytics nello stesso momento, non verrebbero inseriti più tag identici. Tutte le chiamate riutilizzerebbero la stessa Promise.
Gli eventi onload e onerror permettono inoltre di distinguere un caricamento riuscito da uno fallito. È una pratica importante con servizi esterni, perché una rete instabile, un’estensione del browser o un blocco del dominio possono impedire il download.
Best practice
- Carica solo dopo il consenso: per strumenti non necessari al funzionamento del sito, attendi una scelta esplicita dell’utente quando previsto dalla normativa applicabile.
- Gestisci gli errori: non dare per scontato che un CDN o un servizio esterno sia sempre disponibile.
- Evita duplicati: conserva lo stato del caricamento e non aggiungere più volte lo stesso script.
- Non bloccare l’interfaccia: preferisci caricamento asincrono e inizializzazione successiva per widget, chat e sistemi di misurazione.
- Non inserire dati personali negli URL: parametri come email o identificativi sensibili non devono essere inviati indiscriminatamente a servizi di terze parti.
- Definisci un comportamento alternativo: la pagina deve rimanere utilizzabile anche se lo script esterno non viene caricato.
- Verifica la sicurezza: valuta Content Security Policy, integrità delle risorse quando applicabile e affidabilità del fornitore.
- Controlla gli strumenti per sviluppatori: nella scheda Network puoi verificare quando parte la richiesta e se viene effettuata prima o dopo il consenso.
Riepilogo
La differenza tra script sincroni e asincroni diventa particolarmente importante quando si integrano servizi esterni. Uno script sincrono può interrompere il parsing del documento, mentre uno asincrono riduce il blocco durante il download, ma non garantisce un ordine di esecuzione preciso.
Per Analytics e strumenti simili, una soluzione robusta consiste nel caricare lo script soltanto dopo il consenso, usare il caricamento asincrono, gestire correttamente gli eventi di successo e di errore e impedire caricamenti duplicati. In questo modo si ottengono una pagina più reattiva, un comportamento prevedibile e una migliore gestione della privacy.
