JavaScriptとスレッド


137

JavaScriptでマルチスレッドを実行する方法はありますか?


2
JavaScriptには、少なくとも現在の形式ではスレッドが含まれていません。正確に何をしようとしていますか?
エリックポール

3
これに対する承認済みの回答を変更できますか?stackoverflow.com/a/30891727/2576706それははるかに将来のユーザーのために開発されて...
ルドビクFeltz

回答:


109

最新のサポート情報については、http://caniuse.com/#search=workerを参照してください

以下は2009年頃のサポート状況です。


グーグルしたい言葉はJavaScriptワーカースレッドです

Gearsを除いて、現在利用できるものはありませんが、これを実装する方法についてはたくさんの話し合いがあるので、答えは間違いなく将来変わるので、この質問を見てください。

Gearsの関連ドキュメントは次のとおりです:WorkerPool API

WHATWGには、ワーカースレッドに関する推奨草案がありますWebワーカー

また、MozillaのDOMワーカースレッドもあります。


更新: 2009年6月、JavaScriptスレッドのブラウザーサポートの現状

Firefox 3.5にはWebワーカーがあります。Webワーカーのデモをいくつか紹介します(実際に見てみたい場合)。

GearsプラグインはFirefoxにもインストールできます。

Safari 4WebKitナイトリーにはワーカースレッドがあります。

ChromeにはGearsが組み込まれているため、スレッドの実行は可能ですが、ユーザーからの確認プロンプトが必要です(Gearsプラグインがインストールされているブラウザーでは機能しますが、Webワーカーには別のAPIを使用します)。

  • Google Gears WorkerPoolデモ(ChromeとFirefoxでテストするには速度が速すぎるため、良い例ではありませんが、IEは動作が遅いため、インタラクションをブロックしていることがわかります)

IE8IE9は、Gearsプラグインがインストールされているスレッドしか実行できません


1
Safari 4はWebワーカーをサポートしていますが、postMessage:hacks.mozilla.org/2009/07/working-smarter-not-harderを介して複雑なオブジェクトの受け渡しをサポートしているのはFirefoxだけのようです。Bespin プロジェクトでの実際の使用に関する投稿の最後の段落を参照してくださいGoogle Gearsの観点からWorker APIを実装し、Safari 4のワーカー実装に不足している機能と、postMessageインターフェース上に透明なカスタムイベントを実装する方法の詳細を追加するシムへのリンク。
サム・ハスラー

6
IE9がリリースされました。「IE8はGearsプラグインがインストールされている場合にのみスレッドを実行できます」を「IE8とIE9はGearsプラグインがインストールされている場合のみスレッドを実行できる」に更新できます
BenoitParis

2
@ inf3rnoは、別のスレッドで長時間の計算を行うため、ブラウザUIの速度を低下させません。
Sam Hasler 2012

6
@SamHasler回答を修正したい場合があります。現在、Webワーカーはすべての最新のデスクトップブラウザーでサポートされています。caniuse.com/#search=worker
Rob W

2
@SamHasler Google Gearsがサポートされなくなったことも注目に値します。
skeggse 2015

73

JavaScriptでマルチスレッドと非同期を実行する別の方法

HTML5より前のJavaScriptでは、ページごとに1つのスレッドしか実行できませんでした。

非同期実行をシミュレートするためにいくつかのハックの方法がありました収量setTimeout()setInterval()XMLHttpRequestまたはイベントハンドラを(と例については、この記事の最後を参照の収量setTimeout())が。

しかし、HTML5では、ワーカースレッドを使用して関数の実行を並列化できるようになりました。こちらが使用例です。


実際のマルチスレッド

マルチスレッド:JavaScriptワーカースレッド

HTML5で導入されたWebワーカースレッド(参照:ブラウザーの互換性
注:IE9以前のバージョンではサポートされていません。

これらのワーカースレッドは、ページのパフォーマンスに影響を与えずにバックグラウンドで実行されるJavaScriptスレッドです。Webワーカーの 詳細については、ドキュメントまたはこのチュートリアルをご覧ください。

以下は、MAX_VALUEまでカウントし、現在の計算値をページに表示する3つのWeb Workerスレッドの簡単な例です。

//As a worker normally take another JavaScript file to execute we convert the function in an URL: http://stackoverflow.com/a/16799132/2576706
function getScriptPath(foo){ return window.URL.createObjectURL(new Blob([foo.toString().match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1]],{type:'text/javascript'})); }

var MAX_VALUE = 10000;

/*
 *	Here are the workers
 */
//Worker 1
var worker1 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
//We add a listener to the worker to get the response and show it in the page
worker1.addEventListener('message', function(e) {
  document.getElementById("result1").innerHTML = e.data;
}, false);


//Worker 2
var worker2 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
worker2.addEventListener('message', function(e) {
  document.getElementById("result2").innerHTML = e.data;
}, false);


//Worker 3
var worker3 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
worker3.addEventListener('message', function(e) {
    document.getElementById("result3").innerHTML = e.data;
}, false);


// Start and send data to our worker.
worker1.postMessage(MAX_VALUE); 
worker2.postMessage(MAX_VALUE); 
worker3.postMessage(MAX_VALUE);
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

3つのスレッドが並行して実行され、現在の値をページに出力していることがわかります。それらは別々のスレッドでバックグラウンドで実行されるため、ページをフリーズしません。


マルチスレッド:複数のiframe

これを実現する別の方法は、複数のiframeを使用することです。それぞれがスレッドを実行します。URLによってiframeにいくつかのパラメーターを与えることができます。結果を取得して印刷するために、iframeは親と通信できます(iframeは同じドメインにある必要があります)。

この例はすべてのブラウザで機能するわけではありません。 iframeは通常、メインページと同じスレッド/プロセスで実行されます(ただし、FirefoxとChromiumでは処理が異なるようです)。

コードスニペットは複数のHTMLファイルをサポートしていないため、ここでは異なるコードを提供します。

index.html:

//The 3 iframes containing the code (take the thread id in param)
<iframe id="threadFrame1" src="thread.html?id=1"></iframe>
<iframe id="threadFrame2" src="thread.html?id=2"></iframe>
<iframe id="threadFrame3" src="thread.html?id=3"></iframe>

//Divs that shows the result
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>


<script>
    //This function is called by each iframe
    function threadResult(threadId, result) {
        document.getElementById("result" + threadId).innerHTML = result;
    }
</script>

thread.html:

//Get the parameters in the URL: http://stackoverflow.com/a/1099670/2576706
function getQueryParams(paramName) {
    var qs = document.location.search.split('+').join(' ');
    var params = {}, tokens, re = /[?&]?([^=]+)=([^&]*)/g;
    while (tokens = re.exec(qs)) {
        params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
    }
    return params[paramName];
}

//The thread code (get the id from the URL, we can pass other parameters as needed)
var MAX_VALUE = 100000;
(function thread() {
    var threadId = getQueryParams('id');
    for(var i=0; i<MAX_VALUE; i++){
        parent.threadResult(threadId, i);
    }
})();

マルチスレッドのシミュレーション

シングルスレッド:setTimeout()でJavaScriptの同時実行性をエミュレートします

「素朴な」方法はsetTimeout()、次のように関数を順番に実行することです。

setTimeout(function(){ /* Some tasks */ }, 0);
setTimeout(function(){ /* Some tasks */ }, 0);
[...]

ただし、この方法は機能しません。各タスクが順番に実行されるためです。

次のように関数を再帰的に呼び出すことにより、非同期実行をシミュレートできます。

var MAX_VALUE = 10000;

function thread1(value, maxValue){
    var me = this;
    document.getElementById("result1").innerHTML = value;
    value++;
  
    //Continue execution
    if(value<=maxValue)
        setTimeout(function () { me.thread1(value, maxValue); }, 0);
}

function thread2(value, maxValue){
    var me = this;
    document.getElementById("result2").innerHTML = value;
    value++;
	
    if(value<=maxValue)
        setTimeout(function () { me.thread2(value, maxValue); }, 0);
}

function thread3(value, maxValue){
    var me = this;
    document.getElementById("result3").innerHTML = value;
    value++;
	
    if(value<=maxValue)
        setTimeout(function () { me.thread3(value, maxValue); }, 0);
}

thread1(0, MAX_VALUE);
thread2(0, MAX_VALUE);
thread3(0, MAX_VALUE);
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

ご覧のとおり、この2番目のメソッドはメインスレッドを使用して関数を実行するため、非常に遅く、ブラウザをフリーズさせます。


シングルスレッド:JavaScriptの同時実行性を歩留まりでエミュレート

Yield ECMAScript 6の新機能で、FirefoxとChromeの最も古いバージョンでのみ機能します(Chromeでは、 chrome:// flags /#enable-javascript-harmonyに表示される試験運用版JavaScriptを有効にする必要があります)。

yieldキーワードを指定すると、ジェネレーター関数の実行が一時停止し、yieldキーワードに続く式の値がジェネレーターの呼び出し元に返されます。これは、ジェネレーターベースのバージョンのreturnキーワードと考えることができます。

ジェネレータを使用すると、関数の実行を一時停止して、後で再開できます。ジェネレーターを使用して、トランポリンと呼ばれる手法で関数をスケジュールできます。

次に例を示します。

var MAX_VALUE = 10000;

Scheduler = {
	_tasks: [],
	add: function(func){
		this._tasks.push(func);
	},	
	start: function(){
		var tasks = this._tasks;
		var length = tasks.length;
		while(length>0){
			for(var i=0; i<length; i++){
				var res = tasks[i].next();
				if(res.done){
					tasks.splice(i, 1);
					length--;
					i--;
				}
			}
		}
	}	
}


function* updateUI(threadID, maxValue) {
  var value = 0;
  while(value<=maxValue){
	yield document.getElementById("result" + threadID).innerHTML = value;
	value++;
  }
}

Scheduler.add(updateUI(1, MAX_VALUE));
Scheduler.add(updateUI(2, MAX_VALUE));
Scheduler.add(updateUI(3, MAX_VALUE));

Scheduler.start()
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>


3
これが本当に最良の答えです。
Jerry Liu

14

HTML5の「サイドスペック」により、setTimeout()、setInterval()などでjavascriptをハッキングする必要がなくなりました

HTML5&Friendsは、javascript Web Workers仕様を導入しています。非同期で独立してスクリプトを実行するためのAPIです。

仕様チュートリアルへのリンク。


11

JavaScriptには本当のスレッドはありません。JavaScriptは柔軟な言語であるため、JavaScriptの一部をエミュレートできます。先日出会ったです。


1
「真のスレッド」とはどういう意味ですか?緑の糸は本当の糸です。
Wes

10

Javascriptには真のマルチスレッドはありませんがsetTimeout()、非同期AJAXリクエストを使用して非同期動作を取得できます。

正確に何を達成しようとしていますか?


7

これはJavascriptでマルチスレッドをシミュレートする方法です

ここで、数値の加算を計算する3つのスレッドを作成します。数値は13で除算でき、数値は10000000000まで3で除算できます。これらの3つの関数は、同時実行性が意味するのと同じ時間に実行できません。しかし、これらの関数を同時に再帰的に実行させるトリックを紹介します:jsFiddle

このコードは私のものです。

体の部分

    <div class="div1">
    <input type="button" value="start/stop" onclick="_thread1.control ? _thread1.stop() : _thread1.start();" /><span>Counting summation of numbers till 10000000000</span> = <span id="1">0</span>
</div>
<div class="div2">
    <input type="button" value="start/stop" onclick="_thread2.control ? _thread2.stop() : _thread2.start();" /><span>Counting numbers can be divided with 13 till 10000000000</span> = <span id="2">0</span>
</div>
<div class="div3">
    <input type="button" value="start/stop" onclick="_thread3.control ? _thread3.stop() : _thread3.start();" /><span>Counting numbers can be divided with 3 till 10000000000</span> = <span id="3">0</span>
</div>

Javascriptパーツ

var _thread1 = {//This is my thread as object
    control: false,//this is my control that will be used for start stop
    value: 0, //stores my result
    current: 0, //stores current number
    func: function () {   //this is my func that will run
        if (this.control) {      // checking for control to run
            if (this.current < 10000000000) {
                this.value += this.current;   
                document.getElementById("1").innerHTML = this.value;
                this.current++;
            }
        }
        setTimeout(function () {  // And here is the trick! setTimeout is a king that will help us simulate threading in javascript
            _thread1.func();    //You cannot use this.func() just try to call with your object name
        }, 0);
    },
    start: function () {
        this.control = true;   //start function
    },
    stop: function () {
        this.control = false;    //stop function
    },
    init: function () {
        setTimeout(function () {
            _thread1.func();    // the first call of our thread
        }, 0)
    }
};
var _thread2 = {
    control: false,
    value: 0,
    current: 0,
    func: function () {
        if (this.control) {
            if (this.current % 13 == 0) {
                this.value++;
            }
            this.current++;
            document.getElementById("2").innerHTML = this.value;
        }
        setTimeout(function () {
            _thread2.func();
        }, 0);
    },
    start: function () {
        this.control = true;
    },
    stop: function () {
        this.control = false;
    },
    init: function () {
        setTimeout(function () {
            _thread2.func();
        }, 0)
    }
};
var _thread3 = {
    control: false,
    value: 0,
    current: 0,
    func: function () {
        if (this.control) {
            if (this.current % 3 == 0) {
                this.value++;
            }
            this.current++;
            document.getElementById("3").innerHTML = this.value;
        }
        setTimeout(function () {
            _thread3.func();
        }, 0);
    },
    start: function () {
        this.control = true;
    },
    stop: function () {
        this.control = false;
    },
    init: function () {
        setTimeout(function () {
            _thread3.func();
        }, 0)
    }
};

_thread1.init();
_thread2.init();
_thread3.init();

この方法がお役に立てば幸いです。


6

コードをステートマシンに変換するコンパイラであるNarrative JavaScriptを使用すると、スレッドを効果的にエミュレートできます。これは、 "yielding"演算子( '->'と表記)を言語に追加して、単一の線形コードブロックで非同期コードを記述できるようにします。



3

生のJavascriptでは、いくつかの非同期呼び出し(xmlhttprequest)を使用するのが最善ですが、これは実際にはスレッド化されておらず、非常に制限されています。 Google Gearsは、ブラウザにいくつかのAPIを追加します。その一部は、スレッドのサポートに使用できます。


1
Google Gears APIは使用できなくなりました。
Ludovic Feltz 2015年

3

AJAX機能を使用できない、または使用したくない場合は、iframeまたは10を使用してください。;)クロスブラウザーの同等の問題やドットネットAJAXなどの構文の問題を心配することなく、マスターページと並行してプロセスをiframeで実行でき、マスターページのJavaScript(インポートしたJavaScriptを含む)をiframe。

たとえば、親iframe内でegFunction()、iframeコンテンツが読み込まれたら、親ドキュメントを呼び出します(非同期部分です)。

parent.egFunction();

必要に応じて、メインのhtmlコードからiframeを動的に生成します。


1
この説明は、私の好みには少し短すぎました。このテクニックの実行方法を詳しく説明するか、いくつかのコードを示すチュートリアルへのリンクを投稿してください。
オリゴフレン2013年

3

別の可能な方法は、JavaScript環境でJavaScriptインタープリターを使用することです。

複数のインタープリターを作成し、メインスレッドからその実行を制御することにより、各スレッドが独自の環境で実行されるマルチスレッドをシミュレートできます。

このアプローチはWebワーカーにいくらか似ていますが、インタープリターにブラウザーのグローバル環境へのアクセスを許可します。

これ実証するために小さなプロジェクトを作りました。

このブログ投稿でのより詳細な説明。


1

JavaScriptにはスレッドはありませんが、ワーカーはあります。

共有オブジェクトが必要ない場合は、ワーカーが適しています。

ほとんどのブラウザ実装では、実際にワーカーをすべてのコアに分散させ、すべてのコアを利用できるようにします。こちらでデモをご覧いただけます

これを非常に簡単に行えるようにするtask.jsというライブラリを開発しました。

task.jsすべてのコア(node.js、およびweb)で実行されるCPU集中型コードを取得するための簡略化されたインターフェース

例は

function blocking (exampleArgument) {
    // block thread
}

// turn blocking pure function into a worker task
const blockingAsync = task.wrap(blocking);

// run task on a autoscaling worker pool
blockingAsync('exampleArgumentValue').then(result => {
    // do something with result
});

0

HTML5 仕様を使用すれば、同じように大量のJSを記述したり、ハックを見つけたりする必要はありません。

HTML5で導入された機能の1つはWebワーカーです。これは、ページのパフォーマンスに影響を与えることなく、他のスクリプトとは独立して、バックグラウンドで実行されるJavaScriptです。

ほとんどすべてのブラウザでサポートされています:

Chrome-4.0以降

IE-10.0以降

Mozilla-3.5以降

Safari-4.0以降

Opera-11.5以降

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.