web-dev-qa-db-fra.com

InnerHTML est-il asynchrone?

J'espère ne pas me moquer de moi-même, 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 les alertes s'affichent avant la mise à jour du HTML (ou peut-être, mais la page n'a pas été rafraîchie/repeinte/peu importe)

Commander ceci codepen pour voir ce que je veux dire.

Veuillez noter que même mettre alert dans setTimeout(..., 0) ne résout pas le problème. On dirait qu'il faut plus de boucles d'événements pour que innerHTML puisse réellement mettre à jour la page.

MODIFIER:

J'ai oublié de mentionner que j'utilise Chrome et je n'ai pas vérifié les autres navigateurs. On dirait que ce n'est visible que dans Chrome. Néanmoins, je suis toujours intéressé par la raison pour laquelle cela se produit.

98
apieceofbart

La définition de innerHTML est synchrone, de même que la plupart des modifications que vous pouvez apporter au DOM. Cependant, le rendu de la page Web est une histoire différente.

(Rappelez-vous que DOM signifie "Document Object Model". C'est juste 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. Le changement de modèle n'est donc pas instantané. changer l'image - la mise à jour prend un peu de temps.)

L'exécution de JavaScript et le rendu de la page Web ont lieu séparément. Pour le dire simplement, tout le code JavaScript de la page est exécuté (de la boucle d'événement, consultez cette excellente vidéo pour plus de détails), puis après que le navigateur rend toute modification à la page Web pour que l'utilisateur puisse voir. C’est la raison pour laquelle le "blocage" est un problème si important: un code informatique très intensif empêche le navigateur de franchir l’étape "Exécuter JS" pour entrer dans l’étape "Afficher la page", ce qui provoque le gel ou le ralentissement de la page.

Le pipeline de Chrome ressemble à ceci:

enter image description here

Comme vous pouvez le constater, tout le JavaScript se passe en premier. Ensuite, la page est stylée, mise en forme, peinte et composée - le "rendu". Tout ce pipeline n'exécutera pas chaque image. Cela dépend des éléments de la page qui ont été 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 lors de 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 pouvez maintenant demander "Tiens-toi, qu'est-ce qui est exécuté dans cette étape" JavaScript "? Tout mon code est-il exécuté 60 fois par seconde?" La réponse est "non", et cela nous ramène au fonctionnement de la boucle d'événements JS. Le code JS ne fonctionne que s'il fait partie de la pile, qu'il s'agisse d'écouteurs d'événements, de délais d'expiration, etc. Voir vidéo précédente (vraiment).

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

129
jered

Oui, il est synchrone, car cela fonctionne (allez-y, tapez-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 qui s'exécute ligne par ligne.

25
d-_-b

La propriété actuelle innerHTML est mise à jour de manière synchrone, mais le rafraîchissement visuel provoqué par cette modification est asynchrone.

Le rendu visuel du DOM est asynchrone dans Chrome et ne se produira pas tant que la pile de fonctions JavaScript en cours n’aura pas été effacée et que le navigateur n’est pas libre d’accepter un nouvel événement. D'autres navigateurs peuvent utiliser des threads distincts pour gérer Le code JavaScript et le rendu du navigateur peuvent laisser certains événements devenir prioritaires pendant qu'une alerte interrompt l'exécution d'un autre événement.

Vous pouvez le voir de deux manières:

  1. Si vous ajoutez for(var i=0; i<1000000; i++) { } avant votre alerte, vous avez donné au navigateur suffisamment de temps pour effectuer un rafraîchissement, mais ce n’est pas le cas, car la pile de fonctions ne s’est pas effacée (add est toujours fonctionnement).

  2. Si vous retardez votre alert via une setTimeout(function() { alert('random'); }, 1) asynchrone, le processus de redessinage va pouvoir passer à la fonction retardée par setTimeout.

    • Cela ne fonctionne pas si vous utilisez un délai d’attente de 0, probablement parce que Chrome donne la priorité à la file d’événements à 0 délais d'attente avant tout autre événement (ou au moins avant les événements redessinés).
6
apsillers