carica ed esegue l'ordine degli script


265

Esistono molti modi diversi per includere JavaScript in una pagina HTML. Conosco le seguenti opzioni:

  • codice incorporato o caricato da URI esterno
  • incluso nel tag <head> o <body> [ 1 , 2 ]
  • con nessuno defero asyncattributo (solo script esterni)
  • incluso in sorgente statica o aggiunto dinamicamente da altri script (in diversi stati di analisi, con metodi diversi)

Senza contare gli onEventscript browser dall'hard disk, javascript: URI e -attributes [ 3 ], ci sono già 16 alternative per far eseguire JS e sono sicuro di aver dimenticato qualcosa.

Non sono così interessato al caricamento rapido (parallelo), sono più curioso dell'ordine di esecuzione (che può dipendere dall'ordine di caricamento e dall'ordine dei documenti ). Esiste un buon riferimento (tra browser) che copre davvero tutti i casi? Ad esempio, http://www.websiteoptimization.com/speed/tweak/defer/ si occupa solo di 6 di essi e verifica principalmente i browser meno recenti.

Come temo non ci sia, ecco la mia domanda specifica: ho alcuni script head (esterni) per l'inizializzazione e il caricamento degli script. Poi ho due script statici incorporati alla fine del corpo. Il primo consente al caricatore di script di aggiungere dinamicamente un altro elemento di script (facendo riferimento a js esterni) al corpo. Il secondo script statico in linea vuole usare js dallo script esterno aggiunto. Può contare sul fatto che l'altro sia stato eseguito (e perché :-)?


Hai mai guardato Caricamento script senza blocco di Steve Souders? È un po 'datato ora, ma contiene ancora alcune preziose informazioni sul comportamento del browser data una specifica tecnica di caricamento degli script.
Josh Habdas,

Risposte:


331

Se non stai caricando dinamicamente gli script o contrassegnandoli come defero async, gli script vengono caricati nell'ordine riscontrato nella pagina. Non importa se si tratta di uno script esterno o di uno script inline: vengono eseguiti nell'ordine in cui si trovano nella pagina. Gli script incorporati che seguono gli script esterni vengono conservati fino a quando tutti gli script esterni che li precedono non sono stati caricati ed eseguiti.

Gli script asincroni (indipendentemente dal modo in cui sono specificati come asincroni) vengono caricati ed eseguiti in un ordine imprevedibile. Il browser li carica in parallelo ed è gratuito eseguirli nell'ordine desiderato.

Non esiste un ordine prevedibile tra più cose asincrone. Se uno necessitava di un ordine prevedibile, dovrebbe essere codificato registrandosi per le notifiche di carico dagli script asincroni e sequenziando manualmente le chiamate javascript quando vengono caricate le cose appropriate.

Quando un tag di script viene inserito in modo dinamico, il comportamento dell'ordine di esecuzione dipenderà dal browser. Puoi vedere come si comporta Firefox in questo articolo di riferimento . In breve, le versioni più recenti di Firefox impostano in modo asincrono un tag di script aggiunto dinamicamente, a meno che il tag di script non sia stato impostato diversamente.

Un tag di script con asyncpuò essere eseguito non appena viene caricato. In effetti, il browser potrebbe mettere in pausa il parser da qualsiasi altra cosa stesse facendo ed eseguire quello script. Quindi, può davvero funzionare in qualsiasi momento. Se lo script fosse memorizzato nella cache, potrebbe essere eseguito quasi immediatamente. Se il caricamento dello script richiede un po 'di tempo, potrebbe essere eseguito al termine dell'analisi. L'unica cosa da ricordare asyncè che può funzionare in qualsiasi momento e che il tempo non è prevedibile.

Un tag di script deferattende fino a quando non viene eseguito l'intero parser e quindi esegue tutti gli script contrassegnati defernell'ordine in cui sono stati rilevati. Ciò consente di contrassegnare diversi script che dipendono l'uno dall'altro defer. Verranno tutti posticipati fino al termine dell'analisi del documento, ma verranno eseguiti nell'ordine in cui sono stati rilevati preservando le loro dipendenze. Penso che defergli script vengano rilasciati in una coda che verrà elaborata al termine dell'analisi. Tecnicamente, il browser potrebbe scaricare gli script in background in qualsiasi momento, ma non eseguiranno o bloccheranno il parser fino a quando il parser non avrà terminato l'analisi della pagina e l'analisi e l'esecuzione di eventuali script incorporati che non sono contrassegnati defero async.

Ecco una citazione da quell'articolo:

gli script inseriti negli script vengono eseguiti in modo asincrono in IE e WebKit, ma in modo sincrono in Opera e Firefox precedente alla 4.0.

La parte pertinente delle specifiche HTML5 (per i browser più recenti conformi) è qui . C'è molto scritto lì sul comportamento asincrono. Ovviamente, questa specifica non si applica ai browser più vecchi (o browser non conformi) il cui comportamento dovresti probabilmente testare per determinare.

Un preventivo dalle specifiche HTML5:

Quindi, deve essere seguita la prima delle seguenti opzioni che descrive la situazione:

Se l'elemento ha un attributo src e l'elemento ha un attributo differito e l'elemento è stato contrassegnato come "inserito dal parser" e l'elemento non ha un attributo asincrono L'elemento deve essere aggiunto alla fine dell'elenco di script che verranno eseguiti al termine dell'analisi del documento associato al documento del parser che ha creato l'elemento.

L'attività che l'origine dell'attività di rete inserisce nella coda delle attività una volta completato l'algoritmo di recupero deve impostare il flag "pronto per essere eseguito dal parser" dell'elemento. Il parser gestirà l'esecuzione dello script.

Se l'elemento ha un attributo src e l'elemento è stato contrassegnato come "inserito nel parser" e l'elemento non ha un attributo asincrono L'elemento è lo script di blocco dell'analisi in sospeso del documento del parser che ha creato l'elemento. (Può esistere solo uno di questi script per documento alla volta).

L'attività che l'origine dell'attività di rete inserisce nella coda delle attività una volta completato l'algoritmo di recupero deve impostare il flag "pronto per essere eseguito dal parser" dell'elemento. Il parser gestirà l'esecuzione dello script.

Se l'elemento non ha un attributo src e l'elemento è stato contrassegnato come "inserito dal parser" e il documento del parser HTML o del parser XML che ha creato l'elemento script ha un foglio di stile che blocca gli script L'elemento è il script di blocco dell'analisi in sospeso del documento del parser che ha creato l'elemento. (Può esistere solo uno di questi script per documento alla volta).

Imposta il flag "pronto per essere eseguito dal parser" dell'elemento. Il parser gestirà l'esecuzione dello script.

Se l'elemento ha un attributo src, non ha un attributo asincrono e non ha il flag set "force-async" L'elemento deve essere aggiunto alla fine dell'elenco di script che verranno eseguiti nell'ordine associato il prima possibile con il documento dell'elemento script al momento in cui è stato avviato la preparazione di un algoritmo di script.

L'attività che l'origine dell'attività di rete inserisce nella coda delle attività una volta completato l'algoritmo di recupero deve eseguire i seguenti passaggi:

Se l'elemento non è ora il primo elemento nell'elenco di script che verrà eseguito nell'ordine il più presto possibile a cui è stato aggiunto sopra, quindi contrassegnare l'elemento come pronto ma interrompere questi passaggi senza eseguire ancora lo script.

Esecuzione: eseguire il blocco di script corrispondente al primo elemento di script in questo elenco di script che verrà eseguito nell'ordine il più presto possibile.

Rimuovi il primo elemento da questo elenco di script che verranno eseguiti nell'ordine il più presto possibile.

Se questo elenco di script che verranno eseguiti nell'ordine il più presto possibile non è ancora vuoto e la prima voce è già stata contrassegnata come pronta, tornare indietro al passaggio contrassegnato come esecuzione.

Se l'elemento ha un attributo src L'elemento deve essere aggiunto al set di script che verrà eseguito il prima possibile del documento dell'elemento script nel momento in cui è stato avviato un algoritmo di preparazione dello script.

L'attività che l'origine dell'attività di rete inserisce nella coda delle attività una volta completato l'algoritmo di recupero deve eseguire il blocco di script e quindi rimuovere l'elemento dal set di script che verrà eseguito il prima possibile.

In caso contrario, l'agente utente deve eseguire immediatamente il blocco di script, anche se altri script sono già in esecuzione.


Che dire degli script del modulo Javascript type="module"?

Javascript ora supporta il caricamento del modulo con sintassi come questa:

<script type="module">
  import {addTextToBody} from './utils.mjs';

  addTextToBody('Modules are pretty cool.');
</script>

Oppure, con srcattributo:

<script type="module" src="http://somedomain.com/somescript.mjs">
</script>

A tutti gli script con type="module"viene assegnato automaticamente l' deferattributo. Questo li scarica in parallelo (se non in linea) con altri caricamenti della pagina e quindi li esegue in ordine, ma al termine dell'analisi.

Agli script del modulo può anche essere assegnato l' asyncattributo che eseguirà gli script del modulo in linea il più presto possibile, senza attendere il completamento del parser e non attendere l'esecuzione dello asyncscript in un ordine particolare rispetto ad altri script.

C'è un diagramma temporale piuttosto utile che mostra il recupero e l'esecuzione di diverse combinazioni di script, inclusi gli script del modulo qui in questo articolo: Caricamento del modulo Javascript .


Grazie per la risposta, ma il problema è che lo script viene aggiunto in modo dinamico alla pagina, il che significa che è considerato asincrono . O funziona solo in <head>? E la mia esperienza è anche che sono eseguiti in ordine di documento?
Bergi,

@Bergi - Se viene aggiunto in modo dinamico, è asincrono e l'ordine di esecuzione è indeterminato a meno che non si scriva codice per controllarlo.
jfriend00

Solo, Kolink afferma il contrario ...
Bergi,

@Bergi - OK, ho modificato la mia risposta per dire che gli script asincroni si caricano in un ordine indeterminato. Possono essere caricati in qualsiasi ordine. Se fossi in te, non farei affidamento sul fatto che l'osservazione di Kolink sia sempre così. Conosco nessuno standard che dice che uno script aggiunto dinamicamente deve essere eseguito immediatamente e deve bloccare l'esecuzione di altri script fino a quando non viene caricato. Mi aspetto che dipenda dal browser e forse anche dai fattori ambientali (se lo script è memorizzato nella cache, ecc ...).
jfriend00

1
@RuudLenders - Dipende dall'implementazione del browser. L'incontro con il tag script precedentemente nel documento, ma contrassegnato con deferdà al parser la possibilità di iniziare prima il suo download mentre ne rimanda ancora l'esecuzione. Tieni presente che se hai molti script dallo stesso host, l'avvio del download prima potrebbe effettivamente rallentare il download di altri dallo stesso host (poiché competono per la larghezza di banda) su cui la tua pagina è in attesa (che non lo sono defer), quindi questa potrebbe essere un'arma a doppio taglio.
jfriend00,

13

Il browser eseguirà gli script nell'ordine in cui li trova. Se si chiama uno script esterno, bloccherà la pagina fino a quando lo script non sarà stato caricato ed eseguito.

Per testare questo fatto:

// file: test.php
sleep(10);
die("alert('Done!');");

// HTML file:
<script type="text/javascript" src="test.php"></script>

Gli script aggiunti dinamicamente vengono eseguiti non appena aggiunti al documento.

Per testare questo fatto:

<!DOCTYPE HTML>
<html>
<head>
    <title>Test</title>
</head>
<body>
    <script type="text/javascript">
        var s = document.createElement('script');
        s.type = "text/javascript";
        s.src = "link.js"; // file contains alert("hello!");
        document.body.appendChild(s);
        alert("appended");
    </script>
    <script type="text/javascript">
        alert("final");
    </script>
</body>
</html>

L'ordine degli avvisi è "aggiunto" -> "ciao!" -> "finale"

Se in uno script si tenta di accedere a un elemento che non è stato ancora raggiunto (esempio <script>do something with #blah</script><div id="blah"></div>:), verrà visualizzato un errore.

Complessivamente, sì, puoi includere script esterni e quindi accedere alle loro funzioni e variabili, ma solo se esci dal <script>tag corrente e ne avvii uno nuovo.


Posso confermare quel comportamento. Ma ci sono suggerimenti sulle nostre pagine di feedback, che potrebbero funzionare solo quando test.php è memorizzato nella cache. Conosci qualche link di specifica / riferimento al riguardo?
Bergi,

4
link.js non sta bloccando. Usa uno script simile al tuo php per simulare un lungo tempo di download.
1983,

14
Questa risposta non è corretta Non è sempre il caso che "gli script aggiunti dinamicamente vengano eseguiti non appena aggiunti al documento". A volte questo è vero (ad esempio per le vecchie versioni di Firefox), ma di solito non lo è. L'ordine di esecuzione, come indicato nella risposta di jfriend00, non è determinato.
Fabio Beltramini,

1
Non ha senso che gli script vengano eseguiti nell'ordine in cui appaiono sulla pagina indipendentemente dal fatto che siano in linea o meno. Perché quindi lo snippet di tag manager di Google e molti altri che ho visto, avrebbero il codice per inserire un nuovo script sopra tutti gli altri tag di script nella pagina? Non avrebbe senso farlo, se gli script sopra sono già stati caricati sicuramente ?? Oppure mi sfugge qualcosa.
user3094826


2

Dopo aver testato molte opzioni, ho scoperto che la seguente soluzione semplice sta caricando gli script caricati dinamicamente nell'ordine in cui vengono aggiunti in tutti i browser moderni

loadScripts(sources) {
    sources.forEach(src => {
        var script = document.createElement('script');
        script.src = src;
        script.async = false; //<-- the important part
        document.body.appendChild( script ); //<-- make sure to append to body instead of head 
    });
}

loadScripts(['/scr/script1.js','src/script2.js'])
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.