¿Es innerHTML asincrónico?

101

Espero no hacer el ridículo, pero estoy tratando de entender qué está sucediendo en esas dos líneas de código:

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

Lo que estoy observando es que la alerta se muestra antes de que se actualice HTML (o tal vez lo haya hecho, pero la página no se ha actualizado / repintado / lo que sea)

Consulte este codepen para ver a qué me refiero.

Tenga en cuenta que incluso poniendo alerten setTimeout(..., 0)no ayuda. Parece que se necesitan más bucles de eventos para innerHTMLactualizar la página.

EDITAR:

Olvidé mencionar que estoy usando Chrome y no verifiqué otros navegadores. Parece que solo está visible en Chrome. Sin embargo, todavía estoy interesado en por qué está sucediendo eso.

cada uno
fuente
4
Esto es típico de Chrome.
trincot
2
@trincot gracias! Olvidé mencionar que estoy usando Chrome y no probé con otro navegador antes de preguntar. ¿Tienes alguna referencia?
apieceofbart
16
Cambiar el DOM es sincrónico. La representación del DOM en realidad ocurre después de que se borra la pila de JavaScript. developers.google.com/web/fundamentals/performance/rendering JavaScript> Estilo> Diseño> Pintura> Compuesto. (Al menos para Chrome. Otros navegadores son similares)
jered
2
solo una suposición: el bloqueo de alerta evita que el navegador alcance un paso de repintado, por lo que aunque el DOM ha cambiado, aún no se ha permitido repintar; de nuevo, solo una suposición.
zzzzBov
2
@qbolec mira este video: youtu.be/r8caVE_a5KQ
apieceofbart

Respuestas:

130

La configuración de innerHTML es sincrónica, al igual que la mayoría de los cambios que puede realizar en el DOM. Sin embargo, renderizar la página web es una historia diferente.

(Recuerde, DOM significa "Document Object Model". Es solo un "modelo", una representación de datos. Lo que el usuario ve en su pantalla es una imagen de cómo debería verse ese modelo. Por lo tanto, cambiar el modelo no es instantáneo cambiar la imagen; la actualización lleva algún tiempo).

La ejecución de JavaScript y la representación de la página web en realidad ocurren por separado. Para decirlo de manera simplista, primero se ejecuta todo el JavaScript en la página (desde el bucle de eventos; vea este excelente video para obtener más detalles) y luego, después de eso, el navegador muestra cualquier cambio en la página web para que el usuario lo vea. Esta es la razón por la que "bloquear" es tan importante: ejecutar código computacionalmente intensivo evita que el navegador pase del paso "ejecutar JS" y entre en el paso "renderizar la página", lo que hace que la página se congele o tartamudee.

La canalización de Chrome se ve así:

ingrese la descripción de la imagen aquí

Como puede ver, todo el JavaScript ocurre primero. Luego, la página recibe estilo, diseño, pintura y composición: el "render". No toda esta canalización ejecutará todos los marcos. Depende de los elementos de la página que hayan cambiado, si es que hubo alguno, y de cómo deben volver a reproducirse.

Nota: alert()también es síncrono y se ejecuta durante el paso de JavaScript, por lo que el cuadro de diálogo de alerta aparece antes de que vea los cambios en la página web.

Ahora podría preguntar "Espera, ¿qué se ejecuta exactamente en ese paso de 'JavaScript' en la canalización? ¿Todo mi código se ejecuta 60 veces por segundo?" La respuesta es "no", y se remonta a cómo funciona el bucle de eventos JS. El código JS solo se ejecuta si está en la pila, desde elementos como detectores de eventos, tiempos de espera, lo que sea. Ver video anterior (de verdad).

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

jered
fuente
1
gracias por responder. Conocía algunos detalles sobre el bucle de eventos, etc. Esta canalización tiene mucho sentido, pero como muestran los ejemplos, no es igual en todos los navegadores. Si otros navegadores esperan a que la página se vuelva a pintar antes de mostrar la alerta, eso significa que puede haber una gran demora entre hacer clic en el botón y mostrar la alerta. Intentaré jugar con él más tarde.
apieceofbart
@apieceofbart Otros navegadores también pueden simplemente volver a pintar de forma asincrónica la página mientras se detiene el javascript hasta que el usuario maneja la ventana de alerta. No significa que tengan que esperar a que se vuelva a pintar.
Jonas Schäfer
27

Sí, es sincrónico, porque esto funciona (adelante, escríbalo en su consola):

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

La razón por la que ve la alerta antes de ver el cambio de página es que la renderización del navegador lleva más tiempo y no es tan rápida como su javascript ejecutándose línea por línea.

d -_- b
fuente
Gracias, lo comprobé. Pero tiene que ser algo específico de Chrome: funciona como se espera en otros navegadores. ¿O tal vez es su defecto que esperan a que la página se vuelva a pintar para pasar a la siguiente línea de JavaScript?
apieceofbart
3
¿La alerta muestra el texto correcto? ( texten mi ejemplo) Eso responderá a su pregunta de si es sincrónico. La representación del navegador frente a la ejecución de Javascript es manzana y naranja :)
d -_- b
Lo hace, pero no tiene nada que ver con la pregunta
apieceofbart
1
Su pregunta es literalmente "¿Es innerHTML asincrónico?". Si el valor se puede usar inmediatamente después de que sea síncrono, ¿no? Creo que quieres preguntar más sobre la representación de páginas, no sobre la calidad sincrónica de innerHTML.
d -_- b
8
Sí, la pregunta estaba claramente equivocada, pero no sabía qué preguntar. Si hubiera sabido el correcto, probablemente habría encontrado la respuesta. No quise ser malo, ¡gracias por la respuesta de todos modos!
apieceofbart
6

La innerHTMLpropiedad real se actualiza de forma sincrónica, pero el rediseño visual que provoca este cambio ocurre de forma asincrónica.

La representación visual del DOM es asíncrona en Chrome y no sucederá hasta que la pila de funciones de JavaScript actual se haya borrado y el navegador sea libre de aceptar un nuevo evento. Otros navegadores pueden usar subprocesos separados para manejar el código JavaScript y la representación del navegador, o pueden permitir que algunos eventos tengan prioridad mientras una alerta detiene la ejecución de otro evento.

Puedes ver esto de dos formas:

  1. Si agrega for(var i=0; i<1000000; i++) { }antes de su alerta, le ha dado al navegador suficiente tiempo para volver a dibujar, pero no lo ha hecho, porque la pila de funciones no se ha borrado ( addtodavía se está ejecutando).

  2. Si retrasa su alertvía asincrónica setTimeout(function() { alert('random'); }, 1), el proceso de redibujo se adelantará a la función retrasada por setTimeout.

    • Esto no funciona si usa un tiempo de espera de 0, posiblemente porque Chrome da prioridad de cola de eventos a los 0tiempos de espera antes de cualquier otro evento (o al menos antes de los eventos de redibujo).
apsillers
fuente
¡Gracias por responder! Tenga en cuenta que incluso setTimeout(func, 1)no funcionó todas las veces, consulte este video: youtu.be/r8caVE_a5KQ
apieceofbart