Javascript sono per loro natura risorse parser blocking, sta a significare che al momento in cui il parser del browser inizia la lettura del codice HTML e incontra uno script, sospende tutte le operazioni per poterne eseguire il codice.
Questa continua interruzione, effettuata innumerevoli volte a seconda del numero di script presenti all’interno della pagina, può significativamente compromettere il tempo di rendering della pagina stessa. Se in più lo script è esterno, ulteriore latenza viene indotta dal download della risorsa.
Per evitare di rendere Javascript parser blocking e snellire il percorso di rendering critico, possono essere utilizzare diverse tecniche tra cui l’utilizzo degli attributi HTML async e defer. Ma qual è la differenza tra async e defer? In quale contesto va utilizzato uno anziché l’altro?
Indice dei contenuti
Async è un attributo che può essere utilizzato solo per script esterni affinché questi vengano eseguiti in modalità asincrona. Quando il parser HTML incontra uno script con attributo async, infatti, non sospende il lavoro di lettura.
Il download dello script procede in parallelo a quello del parser. Quando lo script viene completamente scaricato, la lettura del parser viene interrotta per poter eseguirne il codice per poi riprendere ad esecuzione completata.
Questo consente al browser di accelerare il processo di costruzione del DOM senza attendere il tempo di download. In condizioni normali, infatti, il browser blocca il parser, scarica lo script, esegue il codice e poi riprende. Grazie all’attributo async, il tempo di download non è più una componente di ritardo.
Di seguito un esempio di implementazione dell’attributo async:
<script src="script_esterno.js" async></script>
In presenza di più script async sequenziali viene sempre eseguito quello disponibile per primo, ovvero quello il cui download si è rivelato essere più veloce. Di conseguenza l’attributo async non garantisce che lo script venga eseguito nell’ordine in cui si trova all’interno della pagina HTML e quindi non va assolutamente usato per script che richiedono dipendenze non già caricate altrimenti il verificarsi di errori di race condition sarà molto alto.
Come async, anche defer è un attributo che può essere applicato solo per script esterni. A differenza di async, gli script con attributo defer vengono eseguiti rispettando l’ordine in cui sono presenti all’interno della pagina HTML e vengono eseguiti a fine parsing del codice HTML.
Più tecnicamente, il codice Javascript defer viene eseguito al verificarsi dell’evento DOM interactive ma prima del DOMContentLoaded. Di seguito un esempio di implementazione dell’attributo async:
<script src="script_esterno.js" defer></script>
Non esiste il migliore. La domanda che bisogna porsi è un’altra: quando utilizzare asycn e quando utilizzare defer?
L’attributo async viene solitamente utilizzato per gli script indipendenti, ovvero che non dipendono da altri script, e non necessari per la costruzione del codice della pagina, come ad esempio il codice di Google Analytics e dei social button.
Gli script defer sono anch’essi script che non necessitano di essere eseguiti prima della costruzione del DOM ma essendo eseguiti rispettando l’ordine in cui si trovano all’interno della pagina, non causano problemi di race condition. Inoltre essendo eseguiti prima dell’evento DOMContentLoaded, anche gli script che contengono trigger a questo particolare evento verranno correttamente eseguiti.
In termini di parser blocking, gli script caricati con defer non influiscono minimamente al parsing del codice HTML mentre gli script async potrebbero. Infatti se il download di uno script async termina prima del completamento del DOM, il parser HTML viene momentaneamente bloccato per eseguire il codice.
Non tutti gli script esterni sono adatti per essere caricati con defer. Se nel codice HTML è presente codice Javascript inline che fa riferimento a codice contenuto all’interno del codice esterno in defer, essendo questo eseguito durante il parsing HTML (e quindi prima del codice in defer) genererà un errore o potrà dare vita a problemi di race condition.
Bisogna assicurarsi, dunque, che non vi siano codici inline o parser-blocking che dipendono da codici in defer. In alternativa vanno utilizzate tecniche avanzate di deferring di codice Javascript che rendono asincrona l’esecuzione anche del codice inline.
Esistono diversi plugin gratuiti per effettuare il deferring di Javascript, ne elenco alcuni:
Ma io non sono un amante dei plugin quando il codice necessario per raggiungere l’obiettivo è talmente piccolo e snello che può essere inserito nel file functions.php del tema. Ad esempio il seguente codice inserisce l’attributo defer per tutti gli script che non sono jQuery e funziona solo per gli utenti non attualmente loggati:
function defer_parsing_of_js( $url ) { if( is_user_logged_in() || !strpos( $url, '.js' ) || strpos( $url, 'jquery.js' ) || strpos( $url, 'jquery.min.js' ) ) return $url; return "$url' defer "; } add_filter( 'clean_url', 'defer_parsing_of_js', 11, 1 );
Hai domande, dubbi o perplessità? Commenta l’articolo e riceverai una risposta al più presto!