InnerHTML è asincrono?


102

Spero di non rendermi ridicolo ma sto cercando di capire cosa sta succedendo in quelle due righe di codice:

document.body.innerHTML = 'something';
alert('something else');

Quello che sto osservando è che l'avviso viene visualizzato prima che l'HTML sia stato aggiornato (o forse lo è stato ma la pagina non è stata aggiornata / ridipinta / qualunque cosa)

Dai un'occhiata a questo codice per vedere cosa intendo.

Si prega di notare che, anche mettendo alertin setTimeout(..., 0)non aiuta. Sembra che siano necessari più loop di eventi per innerHTMLaggiornare effettivamente la pagina.

MODIFICARE:

Ho dimenticato di dire che sto usando Chrome e non ho controllato altri browser. Sembra che sia visibile solo in Chrome. Tuttavia sono ancora interessato al motivo per cui sta accadendo.


4
Questo è tipico di Chrome.
trincot

2
@trincot grazie! Ho dimenticato di menzionare che sto usando Chrome e non ho provato altri browser prima di chiedere. Hai qualche referenza?
apieceofbart

16
La modifica del DOM è sincrona. Il rendering del DOM avviene effettivamente dopo che lo stack JavaScript è stato cancellato. developers.google.com/web/fundamentals/performance/rendering JavaScript> Stile> Layout> Dipingi> Composito. (Almeno per Chrome. Altri browser sono simili.)
risposto il

2
solo una supposizione: il blocco dell'avviso impedisce al browser di raggiungere una fase di ridisegno, quindi mentre il DOM è cambiato, non è stato ancora autorizzato a ridipingere; ancora una volta, solo una supposizione.
zzzzBov

2
@qbolec controlla questo video: youtu.be/r8caVE_a5KQ
apieceofbart

Risposte:


131

L'impostazione di innerHTML è sincrona, così come la maggior parte delle modifiche che puoi apportare al DOM. Tuttavia, il rendering della pagina web è una storia diversa.

(Ricorda, DOM sta per "Document Object Model". È solo un "modello", una rappresentazione dei dati. Ciò che l'utente vede sullo schermo è un'immagine di come dovrebbe apparire quel modello. Quindi, cambiare il modello non istantaneamente cambia l'immagine: l'aggiornamento richiede del tempo.)

L'esecuzione di JavaScript e il rendering della pagina Web vengono effettivamente eseguiti separatamente. Per dirla semplicisticamente, in primo luogo tutti i JavaScript sulle piste di pagina (dal ciclo di eventi - controllare questo video eccellente per maggiori dettagli) e poi , dopo che il browser rende eventuali modifiche alla pagina Web per l'utente di vedere. Questo è il motivo per cui il "blocco" è un grosso problema: l'esecuzione di codice ad alta intensità di calcolo impedisce al browser di superare il passaggio "esegui JS" e passare al passaggio "rendering della pagina", causando il blocco o la balbuzie della pagina.

La pipeline di Chrome ha questo aspetto:

inserisci qui la descrizione dell'immagine

Come puoi vedere, tutto il JavaScript viene eseguito per primo. Quindi la pagina viene stilizzata, disposta, dipinta e composta: il "rendering". Non tutta questa pipeline eseguirà ogni frame. Dipende da quali elementi della pagina sono stati modificati, se presenti, e da come è necessario renderli nuovamente.

Nota: alert()è anche sincrono e viene eseguito durante il passaggio JavaScript, motivo per cui la finestra di dialogo di avviso viene visualizzata prima di visualizzare le modifiche alla pagina Web.

Ora potresti chiedere "Aspetta, cosa viene eseguito esattamente in quel passaggio" JavaScript "nella pipeline? Tutto il mio codice viene eseguito 60 volte al secondo?" La risposta è "no" e risale a come funziona il ciclo di eventi JS. Il codice JS viene eseguito solo se è nello stack, da cose come listener di eventi, timeout, qualsiasi cosa. Guarda il video precedente (davvero).

https://developers.google.com/web/fundamentals/performance/rendering/


1
grazie per avermi risposto. Conoscevo alcuni dettagli sul loop di eventi, ecc. Questa pipeline ha perfettamente senso ma come mostrano gli esempi non è la stessa in tutti i browser. Se altri browser attendono che la pagina venga ridisegnata prima di mostrare l'avviso, significa che potrebbe esserci un enorme ritardo tra il clic sul pulsante e la visualizzazione dell'avviso stesso. Proverò a giocarci più tardi.
apieceofbart

@apieceofbart Altri browser possono anche semplicemente ridipingere la pagina in modo asincrono mentre interrompono il materiale javascript fino a quando l'utente non si occupa della finestra di avviso. Ciò non significa che devono aspettare che avvenga la riverniciatura.
Jonas Schäfer

27

Sì, è sincrono, perché funziona (vai avanti, digitalo nella tua console):

document.body.innerHTML = 'text';
alert(document.body.innerHTML);// you will see a 'text' alert

Il motivo per cui vedi l'avviso prima di vedere la pagina che cambia è che il rendering del browser richiede più tempo e non è veloce quanto l'esecuzione di javascript riga per riga.


Grazie, l'ho controllato. Ma deve essere qualcosa di specifico per Chrome: funziona come previsto in altri browser. O forse è il loro difetto che aspettano che la pagina venga ridipinta per passare alla riga successiva di javascript?
apieceofbart

3
L'avviso mostra il testo corretto? ( textnel mio esempio) Questo risponderà alla tua domanda se è sincrono. Il rendering del browser rispetto all'esecuzione di Javascript è mela e arance :)
d -_- b

Non ha nulla a che fare con la domanda
apieceofbart

1
La tua domanda è letteralmente "È innerHTML asincrono?". Se il valore può essere utilizzato immediatamente dopo che è sincrono, no? Penso che tu voglia chiedere di più sul rendering delle pagine, non sulla qualità sincrona di innerHTML.
d -_- b

8
Sì, la domanda era chiaramente sbagliata ma non sapevo cosa chiedere. Se avessi saputo quello giusto se avessi trovato probabilmente la risposta. Non volevo essere cattivo, comunque grazie per la risposta!
apieceofbart

6

La innerHTMLproprietà effettiva viene aggiornata in modo sincrono, ma il ridisegno visivo causato da questa modifica avviene in modo asincrono.

Il rendering visivo del DOM è asincrono in Chrome e non avverrà fino a quando lo stack di funzioni JavaScript corrente non sarà stato cancellato e il browser sarà libero di accettare un nuovo evento. Altri browser potrebbero utilizzare thread separati per gestire il codice JavaScript e il rendering del browser, oppure potrebbero consentire ad alcuni eventi di avere la priorità mentre un avviso interrompe l'esecuzione di un altro evento.

Puoi vederlo in due modi:

  1. Se aggiungi for(var i=0; i<1000000; i++) { }prima del tuo avviso, hai dato al browser tutto il tempo per ridisegnare, ma non è stato così, perché lo stack delle funzioni non è stato cancellato ( addè ancora in esecuzione).

  2. Se ritardi il tuo alerttramite un asincrono setTimeout(function() { alert('random'); }, 1), il processo di ridisegno andrà avanti rispetto alla funzione ritardata da setTimeout.

    • Questo non funziona se utilizzi un timeout di 0, probabilmente perché Chrome dà la priorità alla coda degli eventi ai 0timeout prima di qualsiasi altro evento (o almeno prima degli eventi di ridisegno).

Grazie per avermi risposto! Per favore, non che anche setTimeout(func, 1)non ha funzionato ogni volta, guarda questo video: youtu.be/r8caVE_a5KQ
apieceofbart
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.