1. ホーム
  2. javascript

[解決済み] JavaScriptはシングルスレッドであることが保証されていますか?

2022-03-15 18:17:30

質問

JavaScript は、最近のブラウザの実装ではすべてシングルスレッドであることが知られていますが、これは何らかの規格で規定されているのでしょうか、それとも単なる伝統なのでしょうか?JavaScript は常にシングルスレッドであると仮定しても全く問題ないのでしょうか?

どうすれば解決するの?

いい質問ですね。はい」と答えたいところですが できないんです。

JavaScriptは通常、スクリプト(*)から見える実行スレッドが1つであると考えられており、インラインスクリプト、イベントリスナー、タイムアウトが入力されても、ブロックや関数の終わりから戻るまで完全に制御されたままです。

(*: ブラウザが本当に1つのOSスレッドを使用してJSエンジンを実装しているのか、それともWebWorkersによって他の限定されたスレッドオブエクスキューションが導入されているのかという問題は無視します)。

しかし、現実にはこの とは言い切れない ということです。

最も一般的なケースは、即時イベントです。ブラウザは、あなたのコードによってイベントが発生したときに、すぐにこれを発生させます。

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

の結果 log in, blur, log out IE 以外では これらのイベントは、単に focus() を直接呼び出したために起こるかもしれません。 alert() とか、ポップアップ・ウィンドウを開くとか、フォーカスを移動させるものは何でもいいんです。

これは、他のイベントも発生させることができます。例えば i.onchange リスナーで、入力に何かを入力した後 focus() の呼び出しでフォーカスが解除され、ログの順番は log in, change, blur, log out ただし、Operaでは log in, blur, log out, change となり、IEでは(さらに説明不要の) log in, change, log out, blur .

同様に click() を提供する要素に対して onclick ハンドラは、すべてのブラウザで直ちに実行されます (少なくともこれは一貫しています!)。

(私は、直接 on... イベントハンドラプロパティを使用しても同じことが起こりますが、ここでは addEventListenerattachEvent .)

を実行したにもかかわらず、コードがスレッド化されている間にイベントが発生する可能性がある状況もたくさんあります。 何も を呼び起こすことができます。例を挙げますと

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>

ヒット alert をクリックすると、モーダルダイアログボックスが表示されます。そのダイアログを解除するまで、もうスクリプトは実行されませんね?いいえ。メインウィンドウのサイズを変更すると、次のようになります。 alert in, resize, alert out をテキストエリアに入力します。

Linuxではウィンドウのサイズを好きなだけ変更できます。Windowsではそう簡単ではありませんが、画面の解像度を大きいものから小さいものに変更し、ウィンドウが収まらなくなったらサイズを変更することでできます。

と思うかもしれませんが、たかが resize (そしておそらく、さらにいくつかのような scroll スクリプトがスレッド化されているため、ユーザーがブラウザとアクティブなインタラクションをとっていないときに実行される可能性があります。そして、シングルウィンドウの場合は、あなたの言うとおりかもしれません。しかし、クロスウィンドウのスクリプトを実行すると、すべてが台無しになります。Safari 以外のブラウザでは、ウィンドウ、タブ、フレームがビジー状態のときにすべてブロックされるので、別のドキュメントのコードからドキュメントを操作することができ、別のスレッドで実行されて関連するイベント ハンドラを起動させることができます。

スクリプトがスレッド化されている間に、発生させることができるイベントを発生させることができる場所。

  • モーダルポップアップのとき ( alert , confirm , prompt ) は、Opera以外のすべてのブラウザで、開いています。

  • 期間中 showModalDialog をサポートするブラウザで利用できます。

  • このページのスクリプトはビジー状態かもしれません...」というダイアログボックスが表示され、スクリプトの実行を継続するように選択しても、Operaを除いて、ビジーループの最中でもリサイズやブラーなどのイベントが発生し処理されるようになりました。

  • 以前、Sun Java Plugin を使用した IE で、アプレット上の任意のメソッドを呼び出すと、イベントが発生し、スクリプトが再入力されることがありました。これは常にタイミングに依存するバグで、Sunがその後修正した可能性があります(私は確かにそう願っています)。

  • おそらくもっと。私がこれをテストしてからしばらく経ちますが、ブラウザはその後複雑さを増しています。

要約すると、JavaScriptはほとんどのユーザーにとって、ほとんどの場合、厳密なイベント駆動型のシングルスレッドで実行されているように見えるのです。しかし、実際にはそのようなことはありません。どこまでが単なるバグでどこからが意図的な設計なのかは不明ですが、複雑なアプリケーション、特にクロスウィンドウ/フレームスクリプトのアプリケーションを書いている場合は、この問題が断続的に、しかもデバッグしにくい形であなたを苦しめる可能性は十分にあります。

最悪の場合、すべてのイベントレスポンスを間接化することで、並行処理の問題を解決することができます。イベントが来たら、それをキューに落とし、後で順番にキューを処理するために setInterval 関数を使用します。もしあなたが複雑なアプリケーションで使われることを想定してフレームワークを書いているのであれば、これを行うことは良い手かもしれません。 postMessage は、将来的にはクロスドキュメント・スクリプティングの痛みを和らげてくれることも期待しています。