InnerHTML est-il asynchrone?

102

J'espère que je ne me ridiculiserai pas mais j'essaie de comprendre ce qui se passe dans ces deux lignes de code:

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

Ce que j'observe, c'est que l'alerte s'affiche avant que HTML n'ait été mis à jour (ou peut-être que la page n'a pas été rafraîchie / repeinte / quoi que ce soit)

Consultez ce codepen pour voir ce que je veux dire.

Veuillez noter que même la mise alerten place setTimeout(..., 0)n'aide pas. On dirait qu'il faut plus de boucles d'événements pour mettre innerHTMLà jour la page.

ÉDITER:

J'ai oublié de mentionner que j'utilise Chrome et je n'ai pas vérifié les autres navigateurs. Il semble qu'il ne soit visible que dans Chrome. Néanmoins, je suis toujours intéressé par la raison pour laquelle cela se produit.

pièce de bar
la source
4
C'est typique de Chrome.
trincot
2
@trincot merci! J'ai oublié de mentionner que j'utilise Chrome et je n'ai pas essayé un autre navigateur avant de demander. Avez-vous des références?
apieceofbart
16
Changer le DOM est synchrone. Le rendu du DOM se produit en fait après l'effacement de la pile JavaScript. developer.google.com/web/fundamentals/performance/rendering JavaScript> Style> Mise en page> Peinture> Composite. (Au moins pour Chrome. Les autres navigateurs sont similaires.)
jered
2
juste une supposition: l'alerte bloquant empêche le navigateur d'atteindre une étape de repeinture, donc bien que le DOM ait changé, il n'a pas encore été autorisé à repeindre; encore une fois, juste une supposition.
zzzzBov
2
@qbolec regardez cette vidéo: youtu.be/r8caVE_a5KQ
apieceofbart

Réponses:

131

La configuration de innerHTML est synchrone, tout comme la plupart des modifications que vous pouvez apporter au DOM. Cependant, le rendu de la page Web est une autre histoire.

(N'oubliez pas que DOM signifie "Document Object Model". Il s'agit simplement d'un "modèle", une représentation de données. Ce que l'utilisateur voit sur son écran est une image de ce à quoi ce modèle devrait ressembler. Ainsi, changer de modèle ne se fait pas instantanément changer l'image - la mise à jour prend un certain temps.)

L'exécution de JavaScript et le rendu de la page Web se produisent en fait séparément. Pour simplifier, tout d'abord le JavaScript de la page s'exécute (à partir de la boucle d'événements - regardez cette excellente vidéo pour plus de détails), puis après que le navigateur rend toute modification apportée à la page Web pour l'utilisateur de voir. C'est pourquoi le "blocage" est si important - l'exécution de code intensif en calcul empêche le navigateur de passer l'étape "exécuter JS" et de passer à l'étape "rendre la page", provoquant le gel ou le bégaiement de la page.

Le pipeline de Chrome ressemble à ceci:

entrez la description de l'image ici

Comme vous pouvez le voir, tout le JavaScript se produit en premier. Ensuite, la page est stylisée, mise en page, peinte et composée - le «rendu». Tout ce pipeline n'exécutera pas chaque image. Cela dépend des éléments de page modifiés, le cas échéant, et de la manière dont ils doivent être rendus.

Remarque: alert()est également synchrone et s'exécute pendant l'étape JavaScript, c'est pourquoi la boîte de dialogue d'alerte apparaît avant que vous ne voyiez les modifications apportées à la page Web.

Vous pourriez maintenant demander "Attendez, qu'est-ce qui est exactement exécuté dans cette étape 'JavaScript' du pipeline? Est-ce que tout mon code s'exécute 60 fois par seconde?" La réponse est "non", et cela revient au fonctionnement de la boucle d'événements JS. Le code JS ne s'exécute que s'il est dans la pile - à partir d'éléments tels que les écouteurs d'événements, les délais d'attente, etc. Voir la vidéo précédente (vraiment).

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

secoué
la source
1
Merci d'avoir répondu. Je connaissais quelques détails sur la boucle d'événements, etc. Ce pipeline est parfaitement logique, mais comme le montrent les exemples, ce n'est pas la même chose dans tous les navigateurs. Si d'autres navigateurs attendent que la page soit repeinte avant d'afficher l'alerte, cela signifie qu'il peut y avoir un délai énorme entre le clic sur le bouton et l'affichage de l'alerte elle-même. J'essaierai de jouer avec plus tard.
chaque pièce de bar
@apieceofbart Les autres navigateurs peuvent aussi simplement repeindre la page de manière asynchrone tout en arrêtant les éléments javascript jusqu'à ce que l'utilisateur traite la fenêtre d'alerte. Cela ne veut pas dire qu'ils doivent attendre que la peinture se produise.
Jonas Schäfer
27

Oui, c'est synchrone, car cela fonctionne (allez-y, saisissez-le dans votre console):

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

La raison pour laquelle vous voyez l'alerte avant de voir la page changer est que le rendu du navigateur prend plus de temps et n'est pas aussi rapide que votre javascript s'exécutant ligne par ligne.

d -_- b
la source
Merci, j'ai vérifié cela. Mais cela doit être quelque chose de spécifique à Chrome - cela fonctionne comme prévu dans d'autres navigateurs. Ou peut-être que c'est leur défaut d'attendre que la page soit repeinte pour passer à la ligne suivante de javascript?
apieceofbart
3
L'alerte affiche-t-elle le texte correct? ( textdans mon exemple) Cela répondra à votre question de savoir si c'est synchrone. Le rendu du navigateur par rapport à l'exécution de Javascript est pomme et oranges :)
d -_- b
Cela fait mais n'a rien à voir avec la question
apieceofbart
1
Votre question est littéralement "InnerHTML est-il asynchrone?". Si la valeur peut être utilisée immédiatement après avoir été synchrone, non? Je pense que vous voulez en dire plus sur le rendu des pages, pas sur la qualité synchrone de innerHTML.
d -_- b
8
Oui, la question était clairement erronée mais je ne savais pas quoi demander. Si j'avais su le bon, j'aurais probablement trouvé la réponse. Je ne voulais pas être méchant, merci pour la réponse quand même!
apieceofbart
6

La innerHTMLpropriété réelle est mise à jour de manière synchrone, mais la mise à jour visuelle provoquée par cette modification se produit de manière asynchrone.

Le rendu visuel du DOM est asynchrone dans Chrome et ne se produira qu'après que la pile de fonctions JavaScript actuelle se soit effacée et que le navigateur soit libre d'accepter un nouvel événement. D'autres navigateurs peuvent utiliser des threads séparés pour gérer le code JavaScript et le rendu du navigateur, ou ils peuvent laisser certains événements avoir la priorité pendant qu'une alerte stoppe l'exécution d'un autre événement.

Vous pouvez voir cela de deux manières:

  1. Si vous ajoutez for(var i=0; i<1000000; i++) { }avant votre alerte, vous avez laissé au navigateur suffisamment de temps pour effectuer un rafraîchissement, mais ce n'est pas le cas, car la pile de fonctions n'a pas été effacée (elle addest toujours en cours d'exécution).

  2. Si vous retardez votre alertvia un setTimeout(function() { alert('random'); }, 1)processus asynchrone , le processus de rafraîchissement pourra avancer la fonction retardée par setTimeout.

    • Cela ne fonctionne pas si vous utilisez un délai d'expiration de 0, peut-être parce que Chrome donne la priorité à la file d' 0attente d'événements aux délais d' attente avant tout autre événement (ou au moins avant les événements de rafraîchissement).
apsillers
la source
Merci d'avoir répondu! Veuillez setTimeout(func, 1)
noter