mdn loop javascript dom event-loop

javascript - loop - ¿Por qué setTimeout(fn, 0) es a veces útil?



settimeout javascript (17)

Ambas de estas dos mejores respuestas están equivocadas. Verifique la descripción de MDN en el modelo de concurrencia y el bucle de eventos , y debería quedar claro lo que está pasando (ese recurso MDN es una verdadera joya). Y simplemente usando setTimeout puede agregar problemas inesperados en su código además de "resolver" este pequeño problema.

Lo que realmente está sucediendo aquí no es que "el navegador aún no esté del todo listo debido a la concurrencia", o algo basado en "cada línea es un evento que se agrega al final de la cola".

El http://jsfiddle.net/C2YBE/31/ proporcionado por DVK ilustra un problema, pero su explicación no es correcta.

Lo que está sucediendo en su código es que primero está adjuntando un controlador de eventos al evento click en el botón #do .

Luego, cuando realmente hace clic en el botón, se crea un message hace referencia a la función del controlador de eventos, que se agrega a la message queue . Cuando el event loop llega a este mensaje, crea un frame en la pila, con la llamada de función al controlador de eventos de clic en el jsfiddle.

Y aquí es donde se pone interesante. Estamos tan acostumbrados a pensar que Javascript es asíncrono que estamos dispuestos a pasar por alto este pequeño dato: cualquier fotograma debe ejecutarse, en su totalidad, antes de que se pueda ejecutar el siguiente fotograma . Sin concurrencia, gente.

¿Qué significa esto? Significa que cada vez que se invoca una función desde la cola de mensajes, bloquea la cola hasta que la pila que genera se haya vaciado. O, en términos más generales, bloquea hasta que la función haya regresado. Y bloquea todo , incluidas las operaciones de representación de DOM, el desplazamiento y otras cosas. Si desea confirmación, solo intente aumentar la duración de la operación de ejecución prolongada en el violín (p. Ej., Ejecute el bucle externo 10 veces más), y notará que, mientras se ejecuta, no puede desplazarse por la página. Si se ejecuta el tiempo suficiente, su navegador le preguntará si desea detener el proceso, ya que la página no responde. El marco se está ejecutando y el bucle de eventos y la cola de mensajes se atascan hasta que finaliza.

Entonces, ¿por qué este efecto secundario del texto no se actualiza? Porque mientras ha cambiado el valor del elemento en el DOM, puede console.log() el valor de console.log() inmediatamente después de cambiarlo y ver que se ha modificado (lo que demuestra por qué la explicación de DVK no es correcta): el navegador está esperando para que la pila se agote (la función del controlador en regresar) y, por lo tanto, el mensaje finalice, para que finalmente pueda ejecutar el mensaje que ha sido agregado por el tiempo de ejecución como una reacción a nuestra operación de mutación, y para Reflejar esa mutación en la interfaz de usuario.

Esto es porque en realidad estamos esperando que el código termine de ejecutarse. No hemos dicho "alguien traiga esto y luego llame a esta función con los resultados, gracias, y ahora he terminado, así que vuelvo, haga lo que sea ahora", como solemos hacer con nuestro Javascript asíncrono basado en eventos. Ingresamos a una función de controlador de eventos de clic, actualizamos un elemento DOM, llamamos a otra función, la otra función funciona durante mucho tiempo y luego regresa, luego actualizamos el mismo elemento DOM y luego regresamos de la función inicial, vaciando efectivamente la pila. Y luego el navegador puede llegar al siguiente mensaje en la cola, que podría ser un mensaje generado por nosotros al desencadenar algún evento interno de tipo "mutación en el DOM".

La interfaz de usuario del navegador no puede (o elige no actualizar) la interfaz de usuario hasta que se complete el marco que se está ejecutando actualmente (la función ha regresado). Personalmente, creo que esto es más bien un diseño que una restricción.

¿Por qué la cosa setTimeout funciona entonces? Lo hace, porque elimina efectivamente la llamada a la función de larga duración de su propio marco, programándola para que se ejecute más tarde en el contexto de la window , de modo que ella misma pueda regresar inmediatamente y permitir que la cola de mensajes procese otros mensajes. Y la idea es que el mensaje de "actualización" de la interfaz de usuario que hemos activado en Javascript al cambiar el texto en el DOM ahora está delante del mensaje en cola para la función de larga duración, de modo que la actualización de la interfaz de usuario ocurra antes de bloquear. por mucho tiempo.

Tenga en cuenta que a) la función de ejecución prolongada aún bloquea todo cuando se ejecuta, yb) no tiene la garantía de que la actualización de la interfaz de usuario esté realmente delante de ella en la cola de mensajes. En mi navegador Chrome de junio de 2018, un valor de 0 no "soluciona" el problema que demuestra el violín: 10 sí. De hecho, me siento un poco sofocado por esto, porque me parece lógico que el mensaje de actualización de la interfaz de usuario se ponga en cola antes que él, ya que su activación se ejecuta antes de programar la función de ejecución prolongada para que se ejecute "más tarde". Pero quizás hay algunas optimizaciones en el motor V8 que pueden interferir, o tal vez no entiendo mi comprensión.

Bien, ¿cuál es el problema con el uso de setTimeout y cuál es la mejor solución para este caso en particular?

En primer lugar, el problema con el uso de setTimeout en cualquier controlador de eventos como este, para tratar de aliviar otro problema, es propenso a meterse con otro código. Aquí hay un ejemplo de la vida real de mi trabajo:

Un colega, en una comprensión mal informada sobre el bucle de eventos, intentó "subprocesar" Javascript al tener un código de representación de plantilla que usa setTimeout 0 para su representación. Ya no está aquí para preguntar, pero puedo suponer que quizás insertó temporizadores para medir la velocidad de reproducción (lo que sería la inmediatez de retorno de las funciones) y descubrió que el uso de este enfoque generaría respuestas increíblemente rápidas de esa función.

El primer problema es obvio; no puede enhebrar javascript, por lo que no gana nada aquí mientras agrega la ofuscación. En segundo lugar, ahora ha separado efectivamente la representación de una plantilla de la pila de posibles escuchas de eventos que podrían esperar que esa misma plantilla se haya procesado, mientras que es muy posible que no lo haya sido. El comportamiento real de esa función ahora no era determinista, como lo era, sin saberlo, cualquier función que la ejecutara o dependiera de ella. Puede hacer conjeturas informadas, pero no puede codificar adecuadamente su comportamiento.

La "solución" al escribir un nuevo controlador de eventos que dependía de su lógica era usar también setTimeout 0 . Pero eso no es una solución, es difícil de entender y no es divertido depurar errores causados ​​por un código como este. A veces, nunca hay problemas, otras veces falla de manera coherente y, de nuevo, a veces funciona y se rompe de forma esporádica, según el rendimiento actual de la plataforma y cualquier otra cosa que suceda en ese momento. Es por eso que personalmente recomendaría no usar este truco ( es un truco, y todos deberíamos saber que lo es), a menos que realmente sepa qué está haciendo y cuáles son las consecuencias.

Pero, ¿qué podemos hacer en su lugar? Bueno, como sugiere el artículo de MDN al que se hace referencia, divida el trabajo en varios mensajes (si puede) para que otros mensajes que están en cola puedan intercalarse con su trabajo y ejecutarse mientras se ejecuta, o utilice un trabajador web, que puede ejecutarse en conjunto con su página y devuelva los resultados cuando termine con sus cálculos.

Ah, y si está pensando: "Bueno, ¿no podría poner una devolución de llamada en la función de ejecución prolongada para hacerla asíncrona?", Entonces no. La devolución de llamada no lo hace asíncrono, todavía tendrá que ejecutar el código de larga duración antes de llamar explícitamente a su devolución de llamada.

Recientemente me he encontrado con un error bastante desagradable, en el que el código se estaba cargando dinámicamente <select> través de JavaScript. Este <select> cargado dinámicamente tenía un valor preseleccionado. En IE6, ya teníamos código para corregir la <option> seleccionada, porque a veces el valor del index <select> no está sincronizado con el atributo de index la <option> seleccionada, como se muestra a continuación:

field.selectedIndex = element.index;

Sin embargo, este código no funcionaba. A pesar de que el índice selectedIndex del campo se estaba configurando correctamente, el índice incorrecto terminaría siendo seleccionado. Sin embargo, si coloco una declaración de alert() en el momento adecuado, se seleccionará la opción correcta. Pensando que esto podría ser algún tipo de problema de tiempo, probé algo al azar que había visto en el código antes:

var wrapFn = (function() { var myField = field; var myElement = element; return function() { myField.selectedIndex = myElement.index; } })(); setTimeout(wrapFn, 0);

¡Y esto funcionó!

Tengo una solución para mi problema, pero me siento incómodo porque no sé exactamente por qué esto soluciona mi problema. ¿Alguien tiene una explicación oficial? ¿Qué problema de navegador estoy evitando al llamar a mi función "más tarde" usando setTimeout() ?


Aquí hay respuestas en contra de votos en conflicto, y sin pruebas no hay manera de saber en quién creer. Aquí hay una prueba de que @DVK es correcto y @SalvadorDali está equivocado. Este último afirma:

"Y aquí es por qué: no es posible tener setTimeout con un retardo de tiempo de 0 milisegundos. El valor mínimo está determinado por el navegador y no es de 0 milisegundos. Históricamente, los navegadores establecen este mínimo de 10 milisegundos, pero las especificaciones de HTML5 y Los navegadores modernos lo tienen configurado en 4 milisegundos ".

El tiempo de espera mínimo de 4 ms es irrelevante para lo que está sucediendo. Lo que realmente sucede es que setTimeout empuja la función de devolución de llamada al final de la cola de ejecución. Si después de setTimeout (devolución de llamada, 0) tiene un código de bloqueo que tarda varios segundos en ejecutarse, la devolución de llamada no se ejecutará durante varios segundos, hasta que el código de bloqueo haya finalizado. Prueba este código:

function testSettimeout0 () { var startTime = new Date().getTime() console.log(''setting timeout 0 callback at '' +sinceStart()) setTimeout(function(){ console.log(''in timeout callback at '' +sinceStart()) }, 0) console.log(''starting blocking loop at '' +sinceStart()) while (sinceStart() < 3000) { continue } console.log(''blocking loop ended at '' +sinceStart()) return // functions below function sinceStart () { return new Date().getTime() - startTime } // sinceStart } // testSettimeout0

La salida es:

setting timeout 0 callback at 0 starting blocking loop at 5 blocking loop ended at 3000 in timeout callback at 3033


Dado que se pasa una duración de 0 , supongo que es para eliminar el código pasado a setTimeout del flujo de ejecución. Por lo tanto, si es una función que podría tomar un tiempo, no impedirá que se ejecute el código subsiguiente.


Eche un vistazo al artículo de John Resig sobre Cómo funcionan los temporizadores de JavaScript . Cuando establece un tiempo de espera, en realidad pone en cola el código asíncrono hasta que el motor ejecuta la pila de llamadas actual.


Esta es una pregunta antigua con respuestas antiguas. Quería agregar una nueva mirada a este problema y responder por qué sucede esto y no por qué es útil.

Así que tienes dos funciones:

var f1 = function () { setTimeout(function(){ console.log("f1", "First function call..."); }, 0); }; var f2 = function () { console.log("f2", "Second call..."); };

y luego llámelos en el siguiente orden f1(); f2(); f1(); f2(); Solo para ver que el segundo se ejecuta primero.

Y aquí es por qué: no es posible tener setTimeout con un retraso de tiempo de 0 milisegundos. El valor mínimo está determinado por el navegador y no es 0 milisegundos. Historically navegadores establecen este mínimo de 10 milisegundos, pero las especificaciones de HTML5 y los navegadores modernos lo tienen configurado en 4 milisegundos.

Si el nivel de anidamiento es mayor que 5 y el tiempo de espera es menor que 4, aumente el tiempo de espera a 4.

También desde mozilla:

Para implementar un tiempo de espera de 0 ms en un navegador moderno, puede usar window.postMessage () como se describe here .

La información de PS se toma después de leer el siguiente article .


Esto funciona porque estás haciendo multitarea cooperativa.

Un navegador tiene que hacer varias cosas casi todas a la vez, y solo una de ellas es ejecutar JavaScript. Pero una de las cosas por las que se usa con mayor frecuencia JavaScript es pedirle al navegador que construya un elemento de visualización. A menudo se asume que esto se realiza de manera síncrona (especialmente porque JavaScript no se ejecuta en paralelo) pero no hay garantía de que así sea, y JavaScript no tiene un mecanismo de espera bien definido.

La solución es "pausar" la ejecución de JavaScript para que los subprocesos de renderización puedan ponerse al día. Y este es el efecto que hace setTimeout() con un tiempo de espera de 0 . Es como un rendimiento de subproceso / proceso en C. Aunque parece decir "ejecutar esto inmediatamente", en realidad le da al navegador la oportunidad de terminar de hacer algunas cosas que no están en JavaScript que han estado esperando para terminar antes de atender esta nueva pieza de JavaScript. .

(En realidad, setTimeout() vuelve a setTimeout() en cola el nuevo JavaScript al final de la cola de ejecución. Consulte los comentarios para obtener una explicación más detallada).

Parece que IE6 es más propenso a este error, pero he visto que ocurre en versiones anteriores de Mozilla y en Firefox.

Vea a Philip Roberts hablar youtube.com/watch?v=8aGhZQkoFbQ para una explicación más completa.


La mayoría de los navegadores tienen un proceso llamado subproceso principal, que se encarga de ejecutar algunas tareas de JavaScript, actualizaciones de la interfaz de usuario, por ejemplo: pintar, volver a dibujar o refluir, etc.

Algunas tareas de ejecución de JavaScript y actualización de la interfaz de usuario se ponen en cola en la cola de mensajes del navegador, y luego se envían al hilo principal del navegador para su ejecución.

Cuando se generan actualizaciones de UI mientras el hilo principal está ocupado, las tareas se agregan a la cola de mensajes.

setTimeout(fn, 0); agregue este fn al final de la cola para ser ejecutado. Programa una tarea para agregarla en la cola de mensajes después de un período de tiempo determinado.


La otra cosa que esto hace es empujar la invocación de la función a la parte inferior de la pila, previniendo un desbordamiento de pila si está recursivamente llamando a una función. Esto tiene el efecto de un bucle while, pero permite que el motor de JavaScript dispare otros temporizadores asíncronos.


Una razón para hacerlo es diferir la ejecución del código a un bucle de eventos posterior y separado. Al responder a un evento de navegador de algún tipo (por ejemplo, hacer clic con el mouse), a veces es necesario realizar operaciones solo después de que se haya procesado el evento actual. La instalación de setTimeout() es la forma más sencilla de hacerlo.

edite ahora que es 2015 Debo tener en cuenta que también existe requestAnimationFrame() , que no es exactamente lo mismo pero es lo suficientemente cerca de setTimeout(fn, 0) que vale la pena mencionar.


setTimeout() le da algo de tiempo hasta que se carguen los elementos DOM, incluso si se establece en 0.

Mira esto: setTimeout


Prefacio:

NOTA IMPORTANTE: aunque es el más votado y aceptado, la respuesta aceptada por @staticsan en realidad NO ES CORRECTA! - vea el comentario de David Mulder para explicación por qué.

Algunas de las otras respuestas son correctas, pero en realidad no ilustran cuál es el problema que se está resolviendo, así que creé esta respuesta para presentar esa ilustración detallada.

Como tal, estoy publicando un recorrido detallado de lo que hace el navegador y cómo el uso de setTimeout() ayuda . Se ve largo, pero en realidad es muy simple y directo, simplemente lo hice muy detallado.

ACTUALIZACIÓN: He hecho un JSFiddle para demostrar en vivo la explicación a continuación: http://jsfiddle.net/C2YBE/31/ . Muchas gracias a @ThangChung por ayudar a ponerlo en marcha.

ACTUALIZACIÓN2: En caso de que el sitio web de JSFiddle muera o borre el código, agregué el código a esta respuesta al final.

DETALLES :

Imagine una aplicación web con un botón de "hacer algo" y un resultado div.

El controlador onClick para el botón "hacer algo" llama a una función "LongCalc ()", que hace 2 cosas:

  1. Hace un cálculo muy largo (digamos que toma 3 min)

  2. Imprime los resultados del cálculo en el div resultado.

Ahora, los usuarios comienzan a probar esto, hacen clic en el botón "hacer algo" y la página se queda allí sin hacer nada durante 3 minutos, se ponen inquietos, vuelven a hacer clic en el botón, esperan 1 minuto, no pasa nada, vuelven a hacer clic en el botón ...

El problema es obvio: desea un DIV de "Estado", que muestra lo que está sucediendo. Vamos a ver cómo funciona eso.

Entonces, agrega un DIV de "Estado" (inicialmente vacío), y modifica el controlador onclick (función LongCalc() ) para hacer 4 cosas:

  1. Rellene el estado "Cálculo ... puede tardar ~ 3 minutos" en estado DIV

  2. Hace un cálculo muy largo (digamos que toma 3 min)

  3. Imprime los resultados del cálculo en el div resultado.

  4. Rellene el estado "Cálculo hecho" en el estado DIV

Y, felizmente le das la aplicación a los usuarios para volver a probar.

Vuelven a verte con mucha ira. ¡Y explique que cuando hicieron clic en el botón, el DIV de estado nunca se actualizó con el estado de "Cálculo ..."!

Te rascas la cabeza, preguntas en (o lees documentos o google) y te das cuenta del problema:

El navegador coloca todas sus tareas "TODO" (tanto las tareas de UI como los comandos de JavaScript) que resultan de los eventos en una sola cola . Y, desafortunadamente, volver a dibujar el DIV "Estado" con el nuevo valor "Calculando ..." es un TODO separado que se dirige al final de la cola.

Aquí hay un desglose de los eventos durante la prueba de su usuario, el contenido de la cola después de cada evento:

  • Cola: [Empty]
  • Evento: Haga clic en el botón. Cola después del evento: [Execute OnClick handler(lines 1-4)]
  • Evento: ejecute la primera línea en el controlador OnClick (por ejemplo, cambie el valor DIV del estado). Cola después del evento: [Execute OnClick handler(lines 2-4), re-draw Status DIV with new "Calculating" value] . Tenga en cuenta que, si bien los cambios de DOM se producen de forma instantánea, para volver a dibujar el elemento DOM correspondiente, necesita un nuevo evento, activado por el cambio de DOM, que se realizó al final de la cola .
  • ¡¡¡PROBLEMA!!! ¡¡¡PROBLEMA!!! Los detalles se explican a continuación.
  • Evento: Ejecutar segunda línea en el controlador (cálculo). Cola después: [Execute OnClick handler(lines 3-4), re-draw Status DIV with "Calculating" value] .
  • Evento: Ejecute la tercera línea en el controlador (rellene el resultado DIV). Cola después: [Execute OnClick handler(line 4), re-draw Status DIV with "Calculating" value, re-draw result DIV with result] .
  • Evento: ejecute la cuarta línea en el controlador (rellene el estado DIV con "DONE"). Cola: [Execute OnClick handler, re-draw Status DIV with "Calculating" value, re-draw result DIV with result; re-draw Status DIV with "DONE" value] [Execute OnClick handler, re-draw Status DIV with "Calculating" value, re-draw result DIV with result; re-draw Status DIV with "DONE" value] .
  • Evento: ejecute el return implícito del onclick . Tomamos el "Ejecutar OnClick handler" de la cola y comenzamos a ejecutar el siguiente elemento en la cola.
  • NOTA: Como ya terminamos el cálculo, ya pasaron 3 minutos para el usuario. ¡El evento de re-draw aún no sucedió!
  • Evento: volver a dibujar el estado DIV con el valor "Cálculo". Hacemos el re-draw y quitamos eso de la cola.
  • Evento: volver a dibujar el resultado DIV con el valor del resultado. Hacemos el re-draw y quitamos eso de la cola.
  • Evento: volver a dibujar el estado DIV con el valor "Hecho". Hacemos el re-draw y quitamos eso de la cola. Los espectadores de ojos agudos pueden incluso notar que el "Estado DIV con el valor de" Cálculo "parpadea durante una fracción de microsegundo: DESPUÉS DEL CÁLCULO TERMINADO

Por lo tanto, el problema subyacente es que el evento de re-draw para el "Estado" DIV se coloca en la cola al final, DESPUÉS del evento de "línea de ejecución 2" que toma 3 minutos, por lo que la re-draw real no ocurre hasta que DESPUÉS se realiza el cálculo.

Al rescate viene el setTimeout() . ¿Cómo ayuda? Porque al llamar a un código de ejecución larga a través de setTimeout , en realidad creas 2 eventos: la ejecución de setTimeout sí misma y (debido a un tiempo de espera de 0), una entrada de cola separada para el código que se está ejecutando.

Entonces, para solucionar su problema, modifica su controlador onClick para que sea DOS sentencias (en una nueva función o simplemente un bloque dentro de onClick ):

  1. Rellene el estado "Cálculo ... puede tardar ~ 3 minutos" en estado DIV

  2. Ejecute setTimeout() con un tiempo de espera de 0 y una llamada a la función LongCalc() .

    LongCalc() función LongCalc() es casi la misma que la última vez, pero obviamente no tiene la actualización DIV del estado "Calculando ..." como primer paso; y en su lugar comienza el cálculo de inmediato.

Entonces, ¿qué aspecto tienen ahora la secuencia de eventos y la cola?

  • Cola: [Empty]
  • Evento: Haga clic en el botón. Cola después del evento: [Execute OnClick handler(status update, setTimeout() call)]
  • Evento: ejecute la primera línea en el controlador OnClick (por ejemplo, cambie el valor DIV del estado). Cola después del evento: [Execute OnClick handler(which is a setTimeout call), re-draw Status DIV with new "Calculating" value] .
  • Evento: Ejecute la segunda línea en el controlador (llamada setTimeout). Queue after: [re-draw Status DIV with "Calculating" value] . La cola no tiene nada nuevo en ella por 0 segundos más.
  • Evento: la alarma del tiempo de espera se apaga, 0 segundos después. Queue after: [re-draw Status DIV with "Calculating" value, execute LongCalc (lines 1-3)] .
  • Evento: volver a dibujar el estado DIV con el valor "Cálculo" . Cola después: [execute LongCalc (lines 1-3)] . Tenga en cuenta que este evento de nuevo dibujo podría suceder ANTES que se active la alarma, lo que también funciona.
  • ...

¡Hurra! El estado DIV se actualizó a "Cálculo ..." antes de que se iniciara el cálculo.

A continuación se muestra el código de ejemplo de JSFiddle que ilustra estos ejemplos: http://jsfiddle.net/C2YBE/31/ :

Código HTML:

<table border=1> <tr><td><button id=''do''>Do long calc - bad status!</button></td> <td><div id=''status''>Not Calculating yet.</div></td> </tr> <tr><td><button id=''do_ok''>Do long calc - good status!</button></td> <td><div id=''status_ok''>Not Calculating yet.</div></td> </tr> </table>

Código JavaScript: (Ejecutado en onDomReady y puede requerir jQuery 1.9)

function long_running(status_div) { var result = 0; // Use 1000/700/300 limits in Chrome, // 300/100/100 in IE8, // 1000/500/200 in FireFox // I have no idea why identical runtimes fail on diff browsers. for (var i = 0; i < 1000; i++) { for (var j = 0; j < 700; j++) { for (var k = 0; k < 300; k++) { result = result + i + j + k; } } } $(status_div).text(''calculation done''); } // Assign events to buttons $(''#do'').on(''click'', function () { $(''#status'').text(''calculating....''); long_running(''#status''); }); $(''#do_ok'').on(''click'', function () { $(''#status_ok'').text(''calculating....''); // This works on IE8. Works in Chrome // Does NOT work in FireFox 25 with timeout =0 or =1 // DOES work in FF if you change timeout from 0 to 500 window.setTimeout(function (){ long_running(''#status_ok'') }, 0); });


Algunos otros casos en los que setTimeout es útil:

Desea dividir un ciclo o cálculo de larga duración en componentes más pequeños para que el navegador no parezca que se "congele" o diga "El script en la página está ocupado".

Desea deshabilitar un botón de envío de formulario cuando se hace clic, pero si deshabilita el botón en el controlador onClick, el formulario no se enviará. setTimeout con un tiempo de cero hace el truco, permitiendo que el evento finalice, que el formulario comience a enviarse, entonces su botón puede ser deshabilitado.


setTimout en 0 también es muy útil en el patrón de configuración de una promesa diferida, que desea devolver de inmediato:

myObject.prototype.myMethodDeferred = function() { var deferredObject = $.Deferred(); var that = this; // Because setTimeout won''t work right with this setTimeout(function() { return myMethodActualWork.call(that, deferredObject); }, 0); return deferredObject.promise(); }


Al llamar a setTimeout, le da tiempo a la página para reaccionar a lo que sea que esté haciendo el usuario. Esto es particularmente útil para las funciones que se ejecutan durante la carga de la página.


El problema era que intentabas realizar una operación de Javascript en un elemento no existente. El elemento aún no se ha cargado y setTimeout()le da más tiempo para que un elemento se cargue de las siguientes maneras:

  1. setTimeout()hace que el evento sea sincrónico, por lo que se ejecuta después de todo el código síncrono, lo que le da a su elemento más tiempo para cargar. Las devoluciones de llamada asíncronas como la devolución de llamada setTimeout()se colocan en la cola de eventos y se colocan en la pila mediante el bucle de eventos una vez que la pila de código síncrono está vacía.
  2. El valor 0 para ms como segundo argumento en la función setTimeout()suele ser un poco más alto (4-10 ms según el navegador). Este tiempo ligeramente superior necesario para ejecutar las setTimeout()devoluciones de llamada se debe a la cantidad de "tics" (donde una marca está empujando una devolución de llamada en la pila si la pila está vacía) del bucle de eventos. Debido al rendimiento y la duración de la batería, la cantidad de tics en el bucle de eventos se restringe a una cierta cantidad de menos de 1000 veces por segundo.

Javascript es una aplicación de un solo subproceso, por lo que no se permite ejecutar la función de forma simultánea, de modo que para lograr este evento se utilizan bucles. Entonces, exactamente qué setTimeout (fn, 0) hace que se encuentre en la búsqueda de tareas que se ejecuta cuando la pila de llamadas está vacía. Sé que esta explicación es bastante aburrida, por lo que te recomiendo que veas este video, esto te ayudará a ver cómo funcionan las cosas en el navegador. Vea este video: - https://www.youtube.com/watch?time_continue=392&v=8aGhZQkoFbQ


Las respuestas sobre los bucles de ejecución y la representación del DOM antes de que se complete algún otro código son correctas. Los segundos tiempos de espera cero en JavaScript ayudan a hacer que el código sea pseudo-multihilo, aunque no lo sea.

Quiero agregar que el MEJOR valor para un tiempo de espera de cero segundos en JavaScript entre plataformas y plataformas cruzadas es en realidad de unos 20 milisegundos en lugar de 0 (cero), ya que muchos navegadores móviles no pueden registrar tiempos de espera inferiores a 20 milisegundos debido a limitaciones de reloj en los chips AMD.

Además, los procesos de larga ejecución que no implican la manipulación de DOM deben enviarse a los Trabajadores Web ahora, ya que proporcionan una verdadera ejecución multihebra de JavaScript.