¿Se garantiza que JavaScript sea de un solo subproceso?
Se sabe que JavaScript tiene un solo subproceso en todas las implementaciones de navegadores modernos, pero ¿está especificado en algún estándar o es solo por tradición? ¿Es totalmente seguro asumir que JavaScript siempre tiene un solo subproceso?
Buena pregunta. Me encantaría decir “sí”. No puedo.
Generalmente se considera que JavaScript tiene un único hilo de ejecución visible para los scripts (*), de modo que cuando se ingresa su script en línea, detector de eventos o tiempo de espera, usted permanece completamente en control hasta que regrese del final de su bloque o función.
(*: ignorando la pregunta de si los navegadores realmente implementan sus motores JS utilizando un subproceso del sistema operativo, o si WebWorkers introduce otros subprocesos de ejecución limitados).
Sin embargo, en realidad esto no es del todo cierto , sino de maneras astutas y desagradables.
El caso más común son los eventos inmediatos. Los navegadores los activarán inmediatamente cuando su código haga algo para causarlos:
var l= document.getElementById('log');
var i= document.getElementById('inp');
i.onblur= function() {
l.value+= 'blur\n';
};
setTimeout(function() {
l.value+= 'log in\n';
l.focus();
l.value+= 'log out\n';
}, 100);
i.focus();
<textarea id="log" rows="20" cols="40"></textarea>
<input id="inp">
Resultados en log in, blur, log out
todos excepto IE. Estos eventos no se activan simplemente porque usted llamó focus()
directamente, sino que también pueden suceder porque llamó a alert()
, abrió una ventana emergente o cualquier otra cosa que mueva el foco.
Esto también puede resultar en otros eventos. Por ejemplo, agregue un i.onchange
oyente y escriba algo en la entrada antes de que la focus()
llamada lo desenfoque, y el orden de registro es log 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
.
De manera similar, llamar click()
a un elemento que lo proporciona llama al onclick
controlador inmediatamente en todos los navegadores (¡al menos esto es consistente!).
(Estoy usando las on...
propiedades del controlador de eventos directo aquí, pero sucede lo mismo con addEventListener
y attachEvent
).
También hay un montón de circunstancias en las que los eventos pueden activarse mientras su código está insertado, a pesar de que no haya hecho nada para provocarlo. Un ejemplo:
var l= document.getElementById('log');
document.getElementById('act').onclick= function() {
l.value+= 'alert in\n';
alert('alert!');
l.value+= 'alert out\n';
};
window.onresize= function() {
l.value+= 'resize\n';
};
<textarea id="log" rows="20" cols="40"></textarea>
<button id="act">alert</button>
Presiona alert
y obtendrás un cuadro de diálogo modal. No se ejecuta más script hasta que descartes ese diálogo, ¿verdad? No. Cambie el tamaño de la ventana principal y accederá alert in, resize, alert out
al área de texto.
Podrías pensar que es imposible cambiar el tamaño de una ventana mientras un cuadro de diálogo modal está abierto, pero no es así: en Linux, puedes cambiar el tamaño de la ventana tanto como quieras; En Windows no es tan fácil, pero puedes hacerlo cambiando la resolución de la pantalla de una más grande a una más pequeña donde la ventana no cabe, lo que hace que cambie de tamaño.
Podrías pensar, bueno, es solo resize
(y probablemente algunos más como scroll
) lo que puede activarse cuando el usuario no tiene interacción activa con el navegador porque el script tiene subprocesos. Y para las ventanas individuales puede que tengas razón. Pero todo eso se arruina tan pronto como estás haciendo secuencias de comandos entre ventanas. Para todos los navegadores distintos de 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 provocando que los controladores de eventos relacionados fuego.
Lugares donde se pueden generar eventos que usted puede provocar que se generen mientras el script aún está enhebrado:
cuando las ventanas emergentes modales (
alert
,, ) están abiertas, en todos los navegadores excepto Operaconfirm
;prompt
durante
showModalDialog
en navegadores que lo admitan;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 tamaño y desenfoque se activen y se manejen incluso mientras el script está en medio de una circuito ocupado, excepto en Opera.
Hace un tiempo para mí, en IE con el complemento Sun Java, llamar a cualquier método en un subprograma podría permitir que se activaran eventos y se volviera a ingresar el script. Este siempre fue un error relacionado con el tiempo, y es posible que Sun lo haya solucionado desde entonces (eso espero).
probablemente más. Ha pasado un tiempo desde que probé esto y los navegadores han ganado complejidad desde entonces.
En resumen, a la mayoría de los usuarios les parece que JavaScript, la mayor parte del tiempo, tiene un único hilo de ejecución estricto basado en eventos. En realidad, no tiene tal cosa. No está claro en qué medida esto es simplemente un error y en qué medida es un diseño deliberado, pero si estás escribiendo aplicaciones complejas, especialmente aquellas que incluyen secuencias de comandos entre ventanas/marcos, hay muchas posibilidades de que te afecte, y de forma intermitente, formas difíciles de depurar.
En el peor de los casos, puede resolver los problemas de concurrencia dirigiendo todas las respuestas a los eventos. Cuando llega un evento, colóquelo en una cola y ocúpese de la cola en orden más adelante, en una setInterval
función. Si está escribiendo un marco que pretende utilizar en aplicaciones complejas, hacerlo podría ser una buena decisión. postMessage
Es de esperar que también alivie el dolor de las secuencias de comandos entre documentos en el futuro.
Yo diría que sí, porque prácticamente todo el código javascript existente (al menos todo el 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), introducir subprocesos múltiples en el Javascript básico sería prácticamente inútil.
( Nota para otros comentaristas: aunque setTimeout/setInterval
los eventos de carga de solicitudes HTTP (XHR) y los eventos de la interfaz de usuario (clic, enfoque, etc.) brindan una cruda impresión de subprocesos múltiples, todavía se ejecutan todos a lo largo de una única línea de tiempo, una a la vez. a la vez, por lo que incluso si no conocemos su orden de ejecución de antemano, no hay necesidad de preocuparse de que las condiciones externas cambien durante la ejecución de un controlador de eventos, una función cronometrada o una devolución de llamada XHR).
Sí, aunque aún puedes sufrir algunos de los problemas de la programación concurrente (principalmente condiciones de carrera) cuando utilizas cualquiera de las API asincrónicas, como las devoluciones de llamada setInterval y xmlhttp.
Sí, aunque Internet Explorer 9 compilará su Javascript en un hilo separado en preparación para su ejecución en el hilo principal. Sin embargo, esto no cambia nada para usted como programador.