Question JavaScript est-il garanti d'être mono-thread?


JavaScript est connu pour être mono-thread dans toutes les implémentations de navigateur modernes, mais est-ce spécifié dans n'importe quelle norme ou est-ce juste par tradition? Est-il totalement sûr de supposer que JavaScript est toujours mono-thread?


547
2018-04-29 00:24


origine


Réponses:


C'est une bonne question. J'aimerais dire "oui". Je ne peux pas.

JavaScript est généralement considéré comme ayant un seul thread d'exécution visible aux scripts (*), de sorte que lorsque vous entrez votre script en ligne, écouteur d'événement ou timeout, vous gardez le contrôle jusqu'à ce que vous reveniez de la fin de votre bloc ou fonction.

(*: en ignorant la question de savoir si les navigateurs implémentent vraiment leurs moteurs JS en utilisant un seul thread OS, ou si d'autres threads d'exécution limités sont introduits par WebWorkers.)

Cependant, en réalité, cette n'est pas tout à fait vrai, de manière méchante et sournoise.

Le cas le plus fréquent est celui des événements immédiats. Les navigateurs les déclencheront immédiatement lorsque votre code fera quelque chose pour les provoquer:

<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>

Résulte en log in, blur, log out sur tous sauf IE. Ces événements ne se déclenchent pas seulement parce que vous avez appelé focus() directement, ils pourraient arriver parce que vous avez appelé alert()ou ouvert une fenêtre contextuelle ou toute autre chose qui déplace le focus.

Cela peut également entraîner d'autres événements. Par exemple ajouter un i.onchange écouteur et tapez quelque chose dans l'entrée avant la focus() appel déconcentre, et l'ordre de journal est log in, change, blur, log out, sauf dans Opera où il est log in, blur, log out, change et IE où c'est (encore moins explicable) log in, change, log out, blur.

De même appeler click() sur un élément qui prévoit qu'il appelle le onclick gestionnaire immédiatement dans tous les navigateurs (au moins c'est cohérent!).

(J'utilise le direct on... propriétés de gestionnaire d'événements ici, mais la même chose se produit avec addEventListener et attachEvent.)

Il y a aussi un tas de circonstances dans lesquelles les événements peuvent se déclencher pendant que votre code est enfilé, même si vous avez terminé rien pour le provoquer. Un exemple:

<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>

Frappé alert et vous obtiendrez une boîte de dialogue modale. Plus de script s'exécute jusqu'à ce que vous rejetiez ce dialogue, oui? Nan. Redimensionnez la fenêtre principale et vous obtiendrez alert in, resize, alert out dans la zone de texte.

Vous pourriez penser qu'il est impossible de redimensionner une fenêtre alors qu'une boîte de dialogue modale est en place, mais ce n'est pas le cas: sous Linux, vous pouvez redimensionner la fenêtre autant que vous le souhaitez; sous Windows, ce n'est pas si simple, mais vous pouvez le faire en changeant la résolution de l'écran d'une plus grande à une plus petite, là où la fenêtre ne rentre pas, ce qui la redimensionne.

Vous pourriez penser, eh bien, c'est seulement resize (et probablement un peu plus comme scroll) qui peut se déclencher lorsque l'utilisateur n'a pas d'interaction active avec le navigateur car le script est threadé. Et pour les fenêtres simples, vous pourriez avoir raison. Mais tout cela va en pot dès que vous faites des scripts cross-window. Pour tous les navigateurs autres que Safari, qui bloque toutes les fenêtres / onglets / cadres quand l'un d'entre eux est occupé, vous pouvez interagir avec un document du code d'un autre document, exécuter dans un thread d'exécution séparé et provoquer tous les gestionnaires d'événements associés Feu.

Les emplacements où les événements que vous pouvez générer peuvent être générés pendant que le script est encore enfilé:

  • quand les popups modaux (alert, confirm, prompt) sont ouverts, dans tous les navigateurs mais Opera;

  • pendant showModalDialog sur les navigateurs qui le supportent;

  • la boîte de dialogue "Un script sur cette page est peut-être occupé ...", même si vous choisissez de laisser le script continuer à s'exécuter, permet de redimensionner des événements comme le redimensionnement et le flou alors que le script est au milieu d'un busy-loop, sauf dans Opera.

  • il y a un moment pour moi, dans IE avec le plugin Sun Java, l'appel de n'importe quelle méthode sur une applet pourrait permettre aux événements de se déclencher et de ré-entrer le script. C'était toujours un bug sensible au timing, et il est possible que Sun l'ait corrigé depuis (je l'espère certainement).

  • probablement plus. Cela fait un moment que je l'ai testé et les navigateurs ont gagné en complexité depuis.

En résumé, JavaScript apparaît à la plupart des utilisateurs, la plupart du temps, pour avoir un seul thread d'exécution strict axé sur les événements. En réalité, il n'a pas une telle chose. On ne sait pas dans quelle mesure il s'agit simplement d'un bug et de la conception délibérée, mais si vous écrivez des applications complexes, en particulier des applications cross-window / frame-scripting, vous avez toutes les chances de vous mordre. difficile à déboguer.

Si le pire se produit, vous pouvez résoudre les problèmes de concurrence en redirigeant toutes les réponses d'événements. Lorsqu'un événement arrive, déposez-le dans une file d'attente et traitez la file d'attente dans l'ordre plus tard, dans un setInterval fonction. Si vous écrivez un framework que vous avez l'intention d'utiliser pour des applications complexes, cela pourrait être un bon choix. postMessage Nous espérons également apaiser la douleur des scripts inter-documents dans le futur.


523
2018-04-29 01:51



Je dirais que oui - parce que la quasi-totalité du code javascript existant (au moins tous non trivial) se briserait si le moteur javascript d'un navigateur l'exécutait de manière asynchrone.

Ajoutez à cela le fait que HTML5 spécifie déjà les Web Workers (une API explicite et standardisée pour le code javascript multithread) introduire le multithreading dans le Javascript de base serait pour la plupart inutile.

(Note aux autres commentateurs: Même si setTimeout/setIntervalLes événements d'en-tête de demande HTTP (XHR) et les événements d'interface utilisateur (clic, focus, etc.) fournissent une impression grossière de multi-thread - ils sont toujours exécutés le long d'une ligne de temps - un à la fois - même si ne connaissant pas leur ordre d'exécution au préalable, il n'y a pas lieu de s'inquiéter de la modification des conditions externes lors de l'exécution d'un gestionnaire d'événements, d'une fonction temporisée ou d'un rappel XHR.)


103
2018-04-29 00:28



Oui, bien que vous puissiez toujours souffrir de certains problèmes de programmation simultanée (principalement des conditions de concurrence) lors de l'utilisation de l'une des API asynchrones telles que les callback setInterval et xmlhttp.


14
2018-04-29 00:28



Oui, bien qu'Internet Explorer 9 compile votre Javascript sur un thread séparé en préparation de l'exécution sur le thread principal. Cela ne change rien pour vous en tant que programmeur, cependant.


10
2018-04-29 00:30



En fait, une fenêtre parente peut communiquer avec des fenêtres ou des cadres enfants ou frères dont les propres threads d'exécution sont en cours d'exécution.


7
2018-04-29 04:03



JavaScript / ECMAScript est conçu pour vivre dans un environnement hôte. Autrement dit, JavaScript ne fait pas faire n'importe quoi à moins que l'environnement hôte décide d'analyser et d'exécuter un script donné et de fournir des objets d'environnement qui permettent à JavaScript d'être utile (comme le DOM dans les navigateurs).

Je pense qu'une fonction donnée ou un bloc de script va s'exécuter ligne par ligne et c'est garanti pour JavaScript. Cependant, un environnement hôte pourrait peut-être exécuter plusieurs scripts en même temps. Ou, un environnement hôte peut toujours fournir un objet qui fournit le multithreading. setTimeout et setInterval sont des exemples, ou au moins des pseudo-exemples, d'un environnement hôte fournissant un moyen de faire une certaine concurrence (même si ce n'est pas exactement la concurrence).


6
2018-04-29 01:09



Je dirais que la spécification n'empêche pas quelqu'un de créer un moteur qui exécute javascript sur plusieurs threads, nécessitant que le code effectue une synchronisation pour accéder à l'état d'objet partagé.

Je pense que le paradigme non bloquant mono-thread est né de la nécessité d'exécuter javascript dans les navigateurs où ui ne devrait jamais bloquer.

Nodejs a suivi l'approche des navigateurs.

Rhinocéros moteur cependant, supporte l'exécution de code js dans différents threads. Les exécutions ne peuvent pas partager le contexte, mais elles peuvent partager la portée. Dans ce cas précis, la documentation indique:

... "Rhino garantit que les accès aux propriétés des objets JavaScript sont atomiques à travers les threads, mais ne donne plus de garanties pour les scripts s'exécutant dans la même portée en même temps. Si deux scripts utilisent la même portée simultanément, les scripts sont responsables de la coordination des accès aux variables partagées"

En lisant la documentation de Rhino, je conclus qu'il est possible pour quelqu'un d'écrire une API javascript qui génère également de nouveaux threads javascript, mais l'API serait spécifique au rhino (par exemple, un nœud ne peut générer qu'un nouveau processus).

J'imagine que même pour un moteur qui supporte plusieurs threads en javascript, il devrait y avoir une compatibilité avec les scripts qui ne considèrent pas le multi-threading ou le blocage.

Concearning navigateurs et nodejs comme je le vois est:

  • Tout le code js est-il exécuté dans un seul thread? : Oui.

  • Le code js peut-il provoquer l'exécution d'autres threads? : Oui.

  • Ces threads peuvent-ils muter le contexte d'exécution de js?: Non. Mais ils peuvent (directement / indirectement (?)) S'ajouter à la file d'attente des événements.

Donc, dans le cas de navigateurs et de nodejs (et probablement beaucoup d'autres moteurs) javascript n'est pas multithread mais les moteurs eux-mêmes le sont.


5
2017-12-12 14:37



Non.

Je vais contre la foule ici, mais supportez-moi. Un seul script JS est destiné à être efficacement un seul thread, mais cela ne signifie pas qu'il ne peut pas être interprété différemment.

Disons que vous avez le code suivant ...

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

Ceci est écrit avec l'espoir qu'à la fin de la boucle, la liste doit avoir 10000 entrées qui sont l'index au carré, mais la VM peut remarquer que chaque itération de la boucle n'affecte pas l'autre, et réinterpréter en utilisant deux threads.

Premier fil

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

Deuxième thread

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

Je simplifie ici, parce que les tableaux JS sont plus compliqués que les morceaux de mémoire, mais si ces deux scripts sont capables d'ajouter des entrées au tableau de manière thread-safe, alors au moment où les deux sont exécutés, ils auront le même résultat que la version à un seul thread.

Bien que je ne connaisse aucune VM détectant un code parallélisable comme celui-ci, il semble probable qu'il pourrait exister dans le futur pour les machines virtuelles JIT, car il pourrait offrir plus de vitesse dans certaines situations.

Prenant ce concept plus loin, il est possible que le code puisse être annoté pour permettre à la VM de savoir ce qu'il faut convertir en code multithread.

// 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;
}

Comme Web Workers vient à Javascript, il est peu probable que ce système ... plus laid apparaisse jamais, mais je pense qu'il est sûr de dire que Javascript est un thread par tradition.


4
2018-02-12 22:02



@Bobince fournit une réponse vraiment opaque.

Riffant la réponse de Már Örlygsson, Javascript est toujours à un seul thread pour ce simple fait: Tout en Javascript est exécuté le long d'une ligne temporelle.

C'est la définition stricte d'un langage de programmation mono-thread.


4
2018-05-25 22:22



Eh bien, Chrome est multiprocesseur, et je pense que chaque processus traite son propre code Javascript, mais pour autant que le code le sache, il est "single-threaded".

Il n'y a aucun support en Javascript pour le multi-threading, du moins pas explicitement, donc ça ne fait pas de différence.


2
2018-04-29 00:28



J'ai essayé l'exemple de @ bobince avec de légères modifications:

<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>

Ainsi, lorsque vous appuyez sur Exécuter, fermez la fenêtre d'alerte et effectuez un "thread unique", vous devriez voir quelque chose comme ceci:

click begin
click end
result = 20, should be 20

Mais si vous essayez d'exécuter cela dans Opera ou Firefox stable sur Windows et de minimiser / agrandir la fenêtre avec popup d'alerte sur l'écran, alors il y aura quelque chose comme ceci:

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

Je ne veux pas dire, que c'est "multithread", mais un bout de code a été exécuté à un mauvais moment avec moi ne m'y attendais pas, et maintenant j'ai un état corrompu. Et mieux vaut savoir sur ce comportement.


1
2017-07-26 12:13