Se sabe que JavaScript tiene un solo subproceso en todas las implementaciones de navegador modernas, pero ¿se especifica eso en algún estándar o es solo por tradición? ¿Es totalmente seguro asumir que JavaScript siempre tiene un solo subproceso?
javascript
concurrency
Egor Pavlikhin
fuente
fuente
Respuestas:
Buena pregunta. Me encantaría decir "sí". No puedo
Por lo general, se considera que JavaScript tiene un solo hilo de ejecución visible para los scripts (*), de modo que cuando se ingresa su script en línea, escucha de eventos o tiempo de espera, usted permanece completamente en control hasta que regrese desde el final de su bloque o función.
(*: ignorando la pregunta de si los navegadores realmente implementan sus motores JS usando un hilo del sistema operativo, o si WebWorkers introduce otros hilos de ejecución limitados).
Sin embargo, en realidad esto no es del todo cierto , de manera astuta y desagradable.
El caso más común son los eventos inmediatos. Los navegadores los activarán de inmediato cuando su código haga algo para causarlos:
Resultados en
log in, blur, log out
todos excepto IE. Estos eventos no solo se disparan porque llamastefocus()
directamente, podrían suceder porque llamastealert()
, abriste una ventana emergente o cualquier otra cosa que mueva el foco.Esto también puede dar lugar a otros eventos. Por ejemplo, agregue un
i.onchange
oyente y escriba algo en la entrada antes de que lafocus()
llamada lo desenfoque, y el orden de registro eslog in, change, blur, log out
, excepto en Opera donde estálog in, blur, log out, change
e IE donde está (aún menos explicable)log in, change, log out, blur
.Del mismo modo, llamar
click()
a un elemento que lo proporciona llama alonclick
controlador de inmediato en todos los navegadores (¡al menos esto es consistente!).(Estoy usando las
on...
propiedades del controlador de eventos directos aquí, pero sucede lo mismo conaddEventListener
yattachEvent
.)También hay un montón de circunstancias en las que los eventos pueden activarse mientras su código está enhebrado, a pesar de que no ha hecho nada para provocarlo. Un ejemplo:
Presiona
alert
y obtendrás un cuadro de diálogo modal. No se ejecutará más script hasta que descarte ese diálogo, ¿sí? No Cambie el tamaño de la ventana principal y accederáalert in, resize, alert out
al área de texto.Puede pensar que es imposible cambiar el tamaño de una ventana mientras está activo un cuadro de diálogo modal, pero no es así: en Linux, puede cambiar el tamaño de la ventana tanto como desee; en Windows no es tan fácil, pero puede hacerlo cambiando la resolución de la pantalla de una más grande a una más pequeña donde la ventana no se ajusta, lo que hace que cambie de tamaño.
Podrías pensar, bueno, es solo
resize
(y probablemente algunos más comoscroll
) que puede activarse cuando el usuario no tiene una interacción activa con el navegador porque el script está enhebrado. Y para ventanas individuales puede que tengas razón. Pero todo se va al bote tan pronto como estás haciendo scripts de ventanas cruzadas. Para todos los navegadores que no sean Safari, que bloquea todas las ventanas / pestañas / marcos cuando alguno de ellos está ocupado, puede interactuar con un documento desde el código de otro documento, ejecutándose en un hilo de ejecución separado y haciendo que cualquier controlador de eventos relacionado fuego.Lugares donde se pueden generar eventos que puede generar que se generen mientras el script aún está enhebrado:
cuando las ventanas emergentes modales (
alert
,confirm
,prompt
) están abiertas, pero en todos los navegadores Opera;durante
showModalDialog
en los navegadores que lo admiten;el cuadro de diálogo "Un script en esta página puede estar ocupado ...", incluso si elige dejar que el script continúe ejecutándose, permite que eventos como cambiar el tamaño y desenfoque se activen mientras el script está en medio de un Loop ocupado, excepto en Opera.
Hace un tiempo para mí, en IE con el complemento Sun Java, llamar a cualquier método en un applet podría permitir que se desencadenaran eventos y se volviera a ingresar el script. Esto siempre fue un error sensible al tiempo, y es posible que Sun lo haya solucionado desde entonces (ciertamente espero que sí).
Probablemente más. Ha pasado un tiempo desde que probé esto y los navegadores han ganado complejidad desde entonces.
En resumen, para la mayoría de los usuarios, la mayoría de las veces, JavaScript tiene un estricto subproceso de ejecución basado en eventos. En realidad, no tiene tal cosa. No está claro cuánto de esto es simplemente un error y cuánto diseño deliberado, pero si está escribiendo aplicaciones complejas, especialmente las de ventana cruzada / scripting de marcos, hay muchas posibilidades de que pueda morderlo, y en forma intermitente, formas difíciles de depurar.
Si lo peor llega a lo peor, puede resolver los problemas de concurrencia indirectando todas las respuestas a eventos. Cuando entra un evento, suéltelo en una cola y trate la cola en orden más adelante, en una
setInterval
función. Si está escribiendo un marco que pretende ser utilizado por aplicaciones complejas, hacer esto podría ser un buen movimiento.postMessage
También esperamos calmar el dolor de las secuencias de comandos de documentos cruzados en el futuro.fuente
blur
después de que su código devuelve el control al navegador.Yo diría que sí, porque prácticamente todo el código javascript existente (al menos no trivial) se rompería si el motor javascript de un navegador lo ejecutara de forma asincrónica.
Agregue a eso el hecho de que HTML5 ya especifica Web Workers (una API explícita y estandarizada para código javascript de subprocesos múltiples) que la introducción de subprocesos múltiples en el Javascript básico no tendría sentido.
( Nota para otros comentaristas: aunque los
setTimeout/setInterval
eventos de carga de solicitud HTTP (XHR) y los eventos de la interfaz de usuario (clic, enfoque, etc.) proporcionan una impresión cruda de multiproceso, todavía se ejecutan a lo largo de una sola línea de tiempo, uno en un tiempo, por lo que incluso si no conocemos su orden de ejecución de antemano, no hay necesidad de preocuparse por las condiciones externas que cambian durante la ejecución de un controlador de eventos, función temporizada o devolución de llamada XHR).fuente
Sí, aunque aún puede sufrir algunos de los problemas de la programación concurrente (principalmente condiciones de carrera) al usar cualquiera de las API asincrónicas como las devoluciones de llamada setInterval y xmlhttp.
fuente
Sí, aunque Internet Explorer 9 compilará su Javascript en un hilo separado en preparación para la ejecución en el hilo principal. Sin embargo, esto no cambia nada para ti como programador.
fuente
Diría que la especificación no impide que alguien cree un motor que ejecute javascript en varios subprocesos , lo que requiere que el código realice la sincronización para acceder al estado del objeto compartido.
Creo que el paradigma de no bloqueo de un solo subproceso surgió de la necesidad de ejecutar javascript en navegadores donde ui nunca debería bloquear.
Nodejs ha seguido el enfoque de los navegadores .
Sin embargo, el motor Rhino admite la ejecución de código js en diferentes subprocesos . Las ejecuciones no pueden compartir el contexto, pero pueden compartir el alcance. Para este caso específico, la documentación establece:
Al leer la documentación de Rhino, llego a la conclusión de que puede ser posible que alguien escriba una API de JavaScript que también genera nuevos hilos de JavaScript, pero la API sería específica de Rhino (por ejemplo, el nodo solo puede generar un nuevo proceso).
Me imagino que incluso para un motor que admite múltiples subprocesos en javascript debería haber compatibilidad con los scripts que no consideran multihilo o bloqueo.
Reconocer navegadores y nodos como lo veo es:
Entonces, en el caso de los navegadores y nodejs (y probablemente muchos otros motores), javascript no es multiproceso, pero los motores sí lo son .
Actualización sobre los trabajadores web:
La presencia de trabajadores web justifica aún más que javascript puede ser multiproceso, en el sentido de que alguien puede crear código en javascript que se ejecutará en un hilo separado.
Sin embargo: los trabajadores web no resuelven los problemas de los hilos tradicionales que pueden compartir el contexto de ejecución. Las reglas 2 y 3 anteriores todavía se aplican , pero esta vez el código creado por el usuario (escritor de código js) en javascript.
Lo único a considerar es la cantidad de hilos generados, desde una eficiencia (y no concurrencia) punto de vista de ). Vea abajo:
Sobre seguridad del hilo :
PD
Además de la teoría, siempre esté preparado sobre posibles casos de esquina y errores descritos en la respuesta aceptada
fuente
JavaScript / ECMAScript está diseñado para vivir dentro de un entorno host. Es decir, JavaScript en realidad no hace nada menos que el entorno del host decida analizar y ejecutar un script determinado y proporcionar objetos de entorno que permitan que JavaScript sea realmente útil (como el DOM en los navegadores).
Creo que una función determinada o un bloque de script se ejecutará línea por línea y eso está garantizado para JavaScript. Sin embargo, quizás un entorno host podría ejecutar múltiples scripts al mismo tiempo. O bien, un entorno host siempre podría proporcionar un objeto que proporcione subprocesos múltiples.
setTimeout
ysetInterval
son ejemplos, o al menos pseudoejemplos, de un entorno host que proporciona una forma de hacer algo de concurrencia (incluso si no es exactamente concurrencia).fuente
En realidad, una ventana principal puede comunicarse con ventanas o marcos secundarios o hermanos que tienen sus propios subprocesos de ejecución en ejecución.
fuente
@Bobince está proporcionando una respuesta realmente opaca.
A partir de la respuesta de Már Örlygsson, Javascript siempre tiene un único subproceso debido a este simple hecho: todo en Javascript se ejecuta a lo largo de una sola línea de tiempo.
Esa es la definición estricta de un lenguaje de programación de un solo subproceso.
fuente
No.
Voy contra la multitud aquí, pero tengan paciencia conmigo. Un solo script JS está destinado a ser efectivamente un solo subproceso, pero esto no significa que no se pueda interpretar de manera diferente.
Digamos que tienes el siguiente código ...
Esto se escribe con la expectativa de que al final del ciclo, la lista debe tener 10000 entradas, que son el índice al cuadrado, pero la VM podría notar que cada iteración del ciclo no afecta al otro y reinterpretar usando dos hilos.
Primer hilo
Segundo hilo
Estoy simplificando aquí, porque las matrices JS son más complicadas que los trozos de memoria tontos, pero si estas dos secuencias de comandos pueden agregar entradas a la matriz de una manera segura para los subprocesos, para cuando ambos terminen de ejecutar, habrá el mismo resultado que la versión de un solo subproceso.
Si bien no conozco ninguna máquina virtual que detecte código paralelo como este, parece probable que pueda surgir en el futuro para las máquinas virtuales JIT, ya que podría ofrecer más velocidad en algunas situaciones.
Llevando este concepto más allá, es posible que el código se pueda anotar para que la VM sepa qué convertir a código multiproceso.
Dado que los Web Workers están llegando a Javascript, es poco probable que este ... sistema más feo alguna vez exista, pero creo que es seguro decir que la tradición tiene un único subproceso.
fuente
Bueno, Chrome es multiproceso, y creo que cada proceso trata con su propio código Javascript, pero hasta donde el código lo sabe, es de "un solo subproceso".
No hay ningún tipo de soporte en Javascript para subprocesos múltiples, al menos no explícitamente, por lo que no hace la diferencia.
fuente
Probé el ejemplo de @bobince con ligeras modificaciones:
Entonces, cuando presione Ejecutar, cierre la ventana emergente de alerta y haga un "hilo único", debería ver algo como esto:
Pero si intenta ejecutar esto en Opera o Firefox estable en Windows y minimizar / maximizar la ventana con una ventana emergente de alerta en la pantalla, entonces habrá algo como esto:
No quiero decir que se trata de "subprocesos múltiples", pero se ejecutó un fragmento de código en un momento incorrecto y no esperaba esto, y ahora tengo un estado corrupto. Y mejor saber sobre este comportamiento.
fuente
Intente anidar dos funciones setTimeout entre sí y se comportarán con subprocesos múltiples (es decir, el temporizador externo no esperará a que se complete el interno antes de ejecutar su función).
fuente
setTimeout(function(){setTimeout(function(){console.log('i herd you liek async')}, 0); alert('yo dawg!')}, 0)
(para el registro, tu dawg SIEMPRE debe ser primero, luego la salida del registro de la consola)