¿Está garantizado que JavaScript sea un subproceso único?


Se sabe que JavaScript es un subproceso único en todas las implementaciones de navegadores modernos, pero ¿se especifica en algún estándar o es solo por tradición? ¿Es totalmente seguro asumir que JavaScript siempre es un subproceso único?

Author: Michał Perłakowski, 2010-04-29

12 answers

Esa es una buena pregunta. Me encantaría decir "sí". No puedo.

Normalmente se considera que JavaScript tiene un único hilo de ejecución visible para los scripts(*), de modo que cuando se introduce el script en línea, el receptor de eventos o el 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 usando un subproceso del sistema operativo, o si otros subprocesos de ejecución limitados son introducido por WebWorkers.)

Sin embargo, en realidad esto no es del todo cierto, en formas furtivas y desagradables.

El caso más común son los eventos inmediatos. Los navegadores activarán estos de inmediato cuando su código haga algo para causarlos:

<textarea id="log" rows="20" cols="40"></textarea>
<input id="inp">
<script type="text/javascript">
    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();
</script>

Resulta en log in, blur, log out en todos excepto IE. Estos eventos no solo se disparan porque llamaste a focus() directamente, podrían suceder porque llamaste a alert(), o abriste una ventana emergente, o cualquier otra cosa que mueva el centrar.

Esto también puede resultar en otros eventos. Por ejemplo, agregue un receptor i.onchange y escriba algo en la entrada antes de que la llamada focus() la desenfoque, y el orden de registro es log in, change, blur, log out, excepto en Opera donde es log in, blur, log out, change e IE donde es (incluso menos explicablemente) log in, change, log out, blur.

De manera similar llamando a click() en un elemento que proporciona llama al controlador onclick inmediatamente en todos los navegadores (al menos esto es consistente!).

(Estoy usando las propiedades directas del controlador de eventos on... aquí, pero lo mismo sucede con addEventListener y attachEvent.)

También hay un montón de circunstancias en las que los eventos pueden disparar mientras tu código está enhebrado, a pesar de que no hayas hecho nada para provocarlo. Un ejemplo:

<textarea id="log" rows="20" cols="40"></textarea>
<button id="act">alert</button>
<script type="text/javascript">
    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';
    };
</script>

Pulsa alert y obtendrás un cuadro de diálogo modal. No se ejecutará más guión hasta que descartes ese diálogo, ¿sí? No. Cambie el tamaño de la ventana principal y obtendrá alert in, resize, alert out en el área de texto.

Usted podría pensar que es imposible cambiar el tamaño de una ventana mientras que un el cuadro de diálogo modal está activado, 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 cabe, haciendo que se cambie el tamaño.

Se podría pensar, bueno, es solo resize (y probablemente algunos más como scroll) que puede disparar cuando el usuario no tiene una interacción activa con el navegador porque el script está enhebrado. Y para las ventanas individuales que podría ser derecho. Pero todo eso va a la olla tan pronto como usted está haciendo cross-window scripting. Para todos los navegadores que no sean Safari, que bloquea todas las ventanas / pestañas / marcos cuando cualquiera de ellos está ocupado, puede interactuar con un documento desde el código de otro documento, ejecutándose en un subproceso de ejecución separado y causando que se activen los controladores de eventos relacionados.

Lugares donde se pueden generar eventos que se pueden generar mientras el script todavía está enhebrado:

  • Cuando el modal ventanas emergentes (alert, confirm, prompt) están abiertos, en todos los navegadores excepto Opera;

  • Durante showModalDialog en los navegadores que lo soportan;

  • El script " A " en esta página puede estar ocupado..."el cuadro de diálogo, incluso si elige dejar que el script continúe ejecutándose, permite que eventos como redimensionar y desenfocar se disparen y se manejen incluso mientras el script está en medio de un bucle ocupado, excepto en Opera.

  • Hace un tiempo para mí, en IE con el plugin Sun Java, llamando a cualquier método en un applet podría permitir que se activen eventos y se vuelva a ingresar el script. Este siempre fue un error sensible al tiempo, y es posible que Sun lo haya arreglado 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, JavaScript parece para la mayoría de los usuarios, la mayoría de las veces, tener un estricto subproceso de ejecución basado en eventos. En realidad, no tiene tal cosa. No está claro cómo gran parte de esto es simplemente un error y cuánto diseño deliberado, pero si está escribiendo aplicaciones complejas, especialmente las de scripting entre ventanas/marcos, hay muchas posibilidades de que lo muerda, y de manera intermitente y difícil de depurar.

Si lo peor llega a lo peor, puede resolver problemas de concurrencia indirectando todas las respuestas de eventos. Cuando entra un evento, colóquelo en una cola y trate con la cola en orden más tarde, en una función setInterval. Si usted está escribiendo un marco que pretende ser utilizado por aplicaciones complejas, hacer esto podría ser un buen paso. postMessage también se espera que alivie el dolor de cross-document scripting en el futuro.

 527
Author: bobince,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-05 22:51:30

Diría que sí, porque prácticamente todo el código javascript existente (al menos todo lo no trivial) se rompería si el motor javascript de un navegador lo ejecutara de forma asíncrona.

Agregue a eso el hecho de que HTML5 ya especifica Web Workers (una API explícita y estandarizada para código javascript multi-threading) introducir multi-threading en el Javascript básico sería en su mayoría inútil.

(Nota para otros comentaristas: Aunque setTimeout/setInterval, los eventos onload de HTTP-request (XHR), y eventos de interfaz de usuario (clic, enfoque, etc.) proporcionar una impresión cruda de multi-threadedness - todavía se ejecutan a lo largo de una sola línea de tiempo - uno a la vez - por lo que incluso si no sabemos 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.)

 104
Author: Már Örlygsson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-29 00:51:18

Sí, aunque todavía puede sufrir algunos de los problemas de programación concurrente (principalmente condiciones de carrera) al usar cualquiera de las API asíncronas como setInterval y xmlhttp callbacks.

 15
Author: spender,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-29 00:28:31

Sí, aunque Internet Explorer 9 compilará su Javascript en un subproceso separado en preparación para la ejecución en el subproceso principal. Sin embargo, esto no cambia nada para ti como programador.

 10
Author: ChessWhiz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-29 00:30:41

JavaScript/ECMAScript está diseñado para vivir dentro de un entorno host. Es decir, JavaScript en realidad no hace nada a menos que el entorno host decida analizar y ejecutar un script dado, y proporcionar objetos de entorno que permitan que JavaScript sea realmente útil (como el DOM en los navegadores).

Creo que una determinada función o bloque de script se ejecutará línea por línea y eso está garantizado para JavaScript. Sin embargo, tal vez un entorno host podría ejecutar varios scripts en al mismo tiempo. O bien, un entorno host siempre podría proporcionar un objeto que proporciona subprocesos múltiples. setTimeout y setInterval son ejemplos, o al menos pseudo ejemplos, de un entorno host que proporciona una forma de hacer cierta concurrencia (incluso si no es exactamente concurrencia).

 7
Author: Bob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-29 01:09:17

En realidad, una ventana padre puede comunicarse con ventanas o marcos hijos o hermanos que tienen sus propios hilos de ejecución en ejecución.

 7
Author: kennebec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-29 04:03:16

Yo diría que la especificación no impide que alguien cree un motor que ejecute javascript en múltiples 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 la interfaz de usuario nunca debería bloquearse.

Nodejs ha seguido el enfoque de los navegadores.

Rhino motor sin embargo, admite la ejecución de código js en diferentes threads . Las ejecuciones no pueden compartir el contexto, pero sí el ámbito. Para este caso específico, la documentación establece:

..."Rhino garantiza que los accesos a las propiedades de los objetos JavaScript son atómicos a través de subprocesos, pero no ofrece más garantías para scripts que se ejecutan en el mismo ámbito al mismo tiempo.Si dos scripts utilizan el mismo ámbito simultáneamente, los scripts son responsables de coordinar cualquier acceso a los variables ."

Al leer la documentación de Rhino, concluyo que puede ser posible que alguien escriba una api de javascript que también genere nuevos subprocesos de javascript, pero la api sería específica de rhino (por ejemplo, node solo puede generar un nuevo proceso).

Me imagino que incluso para un motor que soporta múltiples subprocesos en javascript debe haber compatibilidad con scripts que no consideran multi-threading o bloqueo.

Navegadores Concearning y nodejs el camino Veo que es:

  • ¿Se ejecuta todo el código js en un solo subproceso? : Sí.

  • ¿Puede el código js hacer que se ejecuten otros subprocesos? : Sí.

  • ¿Pueden estos hilos mutar el contexto de ejecución de js?: No. Pero pueden (directa/indirectamente (?)) añadir a la cola de eventos.

Por lo tanto, en el caso de los navegadores y nodejs (y probablemente muchos otros motores) javascript no es multiproceso, pero los motores sí lo son.

 5
Author: Marinos An,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-22 17:41:35

No.

Voy contra la multitud aquí, pero ten 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...

var list = [];
for (var i = 0; i < 10000; i++) {
  list[i] = i * i;
}

Esto se escribe con la expectativa de que al final del bucle, la lista debe tener 10000 entradas que son el índice cuadrado, pero la VM podría notar que cada iteración del bucle no afecta a la otra, y reinterpretar usando dos hilos.

Primer hilo

for (var i = 0; i < 5000; i++) {
  list[i] = i * i;
}

Segundo hilo

for (var i = 5000; i < 10000; i++) {
  list[i] = i * i;
}

Estoy simplificando aquí, porque los arrays JS son más complicados que los trozos tontos de memoria, pero si estos dos scripts son capaces de agregar entradas a la matriz de una manera segura para subprocesos, entonces para cuando ambos terminen de ejecutarlo tendrá el mismo resultado que la versión de subproceso único.

Si bien no estoy al tanto de que ninguna máquina virtual detecte código paralelizable como este, parece probable que pueda venir en existencia en el futuro para 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 pueda ser anotado para que la máquina virtual sepa qué convertir a código multi-threaded.

// like "use strict" this enables certain features on compatible VMs.
"use parallel";

var list = [];

// This string, which has no effect on incompatible VMs, enables threading on
// this loop.
"parallel for";
for (var i = 0; i < 10000; i++) {
  list[i] = i * i;
}

Dado que los trabajadores web están llegando a Javascript, es poco probable que esto... el sistema más feo llegará a existir, pero creo que es seguro decir que Javascript es un solo subproceso por tradición.

 4
Author: max,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-25 03:40:23

@Bobince está proporcionando una respuesta realmente opaca.

Tomando como referencia la respuesta de MárlyLygsson, Javascript siempre tiene un solo 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.

 4
Author: williamle8300,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-25 22:22:38

Bueno, Chrome es multiproceso, y creo que cada proceso se ocupa de su propio código Javascript, pero por lo que el código sabe, es "single-threaded".

No hay soporte en absoluto en Javascript para multi-threading, al menos no explícitamente, por lo que no hace una diferencia.

 2
Author: Francisco Soto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-29 00:40:48

He probado el ejemplo de @bobince con ligeras modificaciones:

<html>
<head>
    <title>Test</title>
</head>
<body>
    <textarea id="log" rows="20" cols="40"></textarea>
    <br />
    <button id="act">Run</button>
    <script type="text/javascript">
        let l= document.getElementById('log');
        let b = document.getElementById('act');
        let s = 0;

        b.addEventListener('click', function() {
            l.value += 'click begin\n';

            s = 10;
            let s2 = s;

            alert('alert!');

            s = s + s2;

            l.value += 'click end\n';
            l.value += `result = ${s}, should be ${s2 + s2}\n`;
            l.value += '----------\n';
        });

        window.addEventListener('resize', function() {
            if (s === 10) {
                s = 5;
            }

            l.value+= 'resize\n';
        });
    </script>
</body>
</html>

Por lo tanto, cuando presione Ejecutar, cierre la ventana emergente de alerta y haga un "hilo único", debería ver algo como esto:

click begin
click end
result = 20, should be 20

Pero si intenta ejecutar esto en Opera o Firefox estable en Windows y minimizar / maximizar la ventana con alerta emergente en la pantalla, entonces habrá algo como esto:

click begin
resize
click end
result = 15, should be 20

No quiero decir, que esto es "multithreading", pero alguna pieza de código se había ejecutado en un momento equivocado conmigo no esperaba esto, y ahora tengo un estado corrupto. Y es mejor saber sobre este comportamiento.

 1
Author: SPYke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-26 12:13:25

Intente anidar dos funciones setTimeout entre sí y se comportarán multiproceso (ie; el temporizador externo no esperará a que el interno se complete antes de ejecutar su función).

 -4
Author: James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-15 19:56:39