#ウェブワーカー
概要
JavaScript 言語はシングルスレッド モデルを使用します。これは、すべてのタスクが 1 つのスレッドでのみ完了でき、一度に実行できることは 1 つだけであることを意味します。前のタスクは完了しておらず、後続のタスクは待つことしかできません。コンピュータの計算能力の向上、特にマルチコア CPU の出現に伴い、シングル スレッドでは大きな不便が生じ、コンピュータの計算能力を十分に活用できなくなりました。
Web ワーカーの役割は、JavaScript 用のマルチスレッド環境を作成し、メインスレッドがワーカー スレッドを作成し、後者にいくつかのタスクを割り当てて実行できるようにすることです。メイン スレッドの実行中、ワーカー スレッドはバックグラウンドで実行され、この 2 つは相互に干渉しません。ワーカー スレッドが計算タスクを完了するまで待ってから、結果をメイン スレッドに返します。この利点は、一部の計算集約型タスクや待ち時間の長いタスクをワーカー スレッドで実行でき、メイン スレッド (通常は UI インタラクションを担当) がブロックされたり速度が低下したりすることなくスムーズな状態を維持できることです。
ワーカー スレッドが正常に作成されると、ワーカー スレッドは常に実行され、メイン スレッド上のアクティビティ (ユーザーによるボタンのクリックやフォームの送信など) によって中断されることはありません。これにより、いつでもメインスレッドの通信に応答できるようになります。ただし、これによりワーカーがより多くのリソースを消費するため、過度に使用すべきではなく、使用後はシャットダウンする必要があります。
Web Worker を使用する場合、以下の注意事項があります。
(1) 同一オリジン制限
ワーカー スレッドに割り当てられるスクリプト ファイルは、メイン スレッドのスクリプト ファイルと同じ起源を持つ必要があります。
(2) DOM の制限事項
Worker スレッドが配置されているグローバル オブジェクトは、メイン スレッドとは異なります。メイン スレッドが配置されている Web ページの DOM オブジェクトを読み取ることはできません。また、document
、window
、および parent
オブジェクトを使用することはできません。ただし、ワーカー スレッドは「navigator」オブジェクトと「location」オブジェクトを使用できます。
(3) グローバル オブジェクトの制限
Worker のグローバル オブジェクト WorkerGlobalScope
は、Web ページのグローバル オブジェクト Window
とは異なり、多くのインターフェイスが利用できません。たとえば、理論的には Worker スレッドは console.log
を使用できません。これは、標準では Worker のグローバル オブジェクトに console
インターフェイスがあることが記載されておらず、Navigator
インターフェイスと Location
インターフェイスだけが定義されているためです。ただし、ブラウザは実際には「console.log」を使用するワーカー スレッドをサポートしているため、この方法は使用しないのが最も安全です。
(4) 連絡事項
ワーカー スレッドとメイン スレッドは同じコンテキストにないため、直接通信することができず、メッセージを通じて完了する必要があります。
(5) スクリプトの制限事項
ワーカー スレッドは alert()
メソッドと confirm()
メソッドを実行できませんが、XMLHttpRequest オブジェクトを使用して AJAX リクエストを行うことができます。
(6) ファイル制限
ワーカー スレッドはローカル ファイルを読み取ることができません。つまり、ローカル ファイル システム (file://
) を開くことができません。ロードするスクリプトはネットワークから取得する必要があります。
基本的な使い方
メインスレッド
メインスレッドは、new
コマンドを使用して Worker()
コンストラクターを呼び出し、新しいワーカー スレッドを作成します。
var ワーカー = 新しいワーカー('work.js');
Worker()
コンストラクターのパラメーターはスクリプト ファイルであり、ワーカー スレッドによって実行されるタスクです。ワーカーはローカル ファイルを読み取ることができないため、このスクリプトはネットワークから取得する必要があります。ダウンロードが失敗した場合 (404 エラーなど)、ワーカーは通知なしで失敗します。
次に、メインスレッドは worker.postMessage()
メソッドを呼び出してワーカーにメッセージを送信します。
worker.postMessage('Hello World');
worker.postMessage({メソッド: 'echo', args: ['Work']});
worker.postMessage()
メソッドのパラメータは、メインスレッドによってワーカーに渡されるデータです。バイナリ データなど、さまざまなデータ型を使用できます。
次に、メインスレッドは worker.onmessage
を通じて listen 関数を指定し、子スレッドから返信されたメッセージを受信します。
worker.onmessage = 関数 (イベント) {
doSomething(event.data);
}
関数 doSomething() {
//タスクを実行する
worker.postMessage('作業完了!');
}
上記のコードでは、イベント オブジェクトの data
属性により、Worker によって送信されたデータを取得できます。
ワーカーがタスクを完了すると、メインスレッドはワーカーをシャットダウンできます。
ワーカー.終了();
ワーカースレッド
「message」イベントをリッスンするには、ワーカー スレッド内にリッスン関数が必要です。
self.addEventListener('メッセージ', function (e) {
self.postMessage('あなたはこう言いました: ' + e.data);
}、 間違い);
上記のコードでは、self
は子スレッド自体、つまり子スレッドのグローバル オブジェクトを表します。したがって、以下の 2 つの書き方に相当します。
//書き方その1
this.addEventListener('メッセージ', function (e) {
this.postMessage('あなたはこう言いました: ' + e.data);
}、 間違い);
//書き方2
addEventListener('メッセージ', function (e) {
postMessage('あなたは言いました: ' + e.data);
}、 間違い);
リスニング関数の指定には self.addEventListener()
を使用するほかに、self.onmessage
を使用して指定することもできます。リスニング関数のパラメータはイベント オブジェクトであり、その data
属性にはメイン スレッドによって送信されたデータが含まれます。 self.postMessage()
メソッドは、メインスレッドにメッセージを送信するために使用されます。
メインスレッドによって送信されたデータに従って、ワーカースレッドはさまざまなメソッドを呼び出すことができます。以下に例を示します。
self.addEventListener('メッセージ', function (e) {
var データ = e.data;
スイッチ (data.cmd) {
ケース「開始」:
self.postMessage('WORKER STARTED: ' + data.msg);
壊す;
'停止'の場合:
self.postMessage('WORKER STOPPED: ' + data.msg);
self.close(); // ワーカーを終了します。
壊す;
デフォルト:
self.postMessage('不明なコマンド: ' + data.msg);
};
}、 間違い);
上記のコードでは、self.close()
を使用して Worker 内で自身を閉じています。
ワーカー読み込みスクリプト
Worker 内に他のスクリプトをロードしたい場合は、特別なメソッド importScripts()
があります。
importScripts('script1.js');
このメソッドでは、複数のスクリプトを同時にロードできます。
importScripts('script1.js', 'script2.js');
エラー処理
メインスレッドはワーカーのエラーを監視できます。エラーが発生した場合、ワーカーはメインスレッドの「error」イベントをトリガーします。
worker.onerror = 関数 (イベント) {
コンソール.log(
'エラー: 行 '、event.lineno、' in '、event.filename、': '、event.message
);
};
// または
worker.addEventListener('error', function (event) {
// ...
});
ワーカーは内部的に「error」イベントをリッスンすることもできます。
クローズワーカー
使用後は、システム リソースを節約するためにワーカーを閉じる必要があります。
// メインスレッド
ワーカー.終了();
// ワーカースレッド
self.close();
データ通信
前述したように、メインスレッドとワーカー間の通信内容はテキストまたはオブジェクトになります。この通信はコピー関係、つまり、ワーカーによる通信内容の変更はメインスレッドに影響を与えないことに注意してください。実際、ブラウザの内部動作メカニズムは、まず通信コンテンツをシリアル化し、次にシリアル化された文字列をワーカーに送信し、ワーカーがそれを復元します。
File、Blob、ArrayBuffer およびその他のタイプのバイナリ データも、メイン スレッドとワーカー間で交換でき、スレッド間で送信することもできます。以下に例を示します。
// メインスレッド
var uInt8Array = new Uint8Array(new ArrayBuffer(10));
for (var i = 0; i < uInt8Array.length; ++i) {
uInt8Array[i] = i * 2; // [0, 2, 4, 6, 8,...]
}
ワーカー.postMessage(uInt8Array);
// ワーカースレッド
self.onmessage = 関数 (e) {
var uInt8Array = e.data;
postMessage('worker.js 内: uInt8Array.toString() = ' + uInt8Array.toString());
postMessage('worker.js 内: uInt8Array.byteLength = ' + uInt8Array.byteLength);
};
ただし、コピー モードでバイナリ データを送信すると、パフォーマンスの問題が発生します。たとえば、メインスレッドが 500MB のファイルをワーカーに送信すると、ブラウザはデフォルトで元のファイルのコピーを生成します。この問題を解決するために、JavaScript では、メインスレッドがバイナリデータを子スレッドに直接転送できるようにしています。ただし、一度転送すると、メインスレッドはこれらのバイナリデータを使用できなくなります。これは、複数のスレッドがデータを変更するという面倒な状況を防ぐためです。同時に。このデータ転送方法は、Transferable Objects と呼ばれます。これにより、メインスレッドはワーカーにデータを迅速に転送できるため、パフォーマンスに負担をかけることなく、画像処理、サウンド処理、3D 操作などに非常に便利です。
データの制御を直接移譲したい場合は、以下の書き込み方法を使用する必要があります。
// 転送可能なオブジェクトの形式
worker.postMessage(arrayBuffer, [arrayBuffer]);
// 例
var ab = 新しい ArrayBuffer(1);
worker.postMessage(ab, [ab]);
同じページ上の Web ワーカー
通常、Worker は別の JavaScript スクリプト ファイルを読み込みますが、メイン スレッドと同じ Web ページにコードを読み込むこともできます。
<!DOCTYPE html>
<本文>
<script id="worker" type="app/worker">
addEventListener('メッセージ', function () {
postMessage('何らかのメッセージ');
}、 間違い);
</script>
</body>
</html>
上記は Web ページに埋め込まれたスクリプトです。<script>
タグの type
属性は、ブラウザーが認識しない値として指定する必要があることに注意してください。上記の例は app/worker
です。
次に、ページに埋め込まれたこのスクリプトを読み取り、Worker を使用して処理します。
var blob = new Blob([document.querySelector('#worker').textContent]);
var url = window.URL.createObjectURL(blob);
var ワーカー = 新しいワーカー(url);
worker.onmessage = 関数 (e) {
// e.data === '何らかのメッセージ'
};
上記のコードでは、Web ページに埋め込まれたスクリプト コードが最初にバイナリ オブジェクトに変換され、次にバイナリ オブジェクトの URL が生成され、ワーカーは URL をロードするように求められます。これで、メイン スレッドとワーカー コードが両方とも同じ Web ページ上に配置されます。
例: ワーカー スレッドがポーリングを完了する
場合によっては、ブラウザーはサーバーのステータスをポーリングして、ステータスの変化をできるだけ早く知ることができるようにする必要があります。この作品はWorkerに配置することができます。
関数 createWorker(f) {
var blob = new Blob(['(' + f.toString() + ')()']);
var url = window.URL.createObjectURL(blob);
var ワーカー = 新しいワーカー(url);
帰還労働者。
}
varpollingWorker = createWorker(function (e) {
var キャッシュ。
関数compare(new, old) { ... };
setInterval(関数() {
fetch('/my-api-endpoint').then(function (res) {
var データ = res.json();
if (!compare(データ, キャッシュ)) {
キャッシュ = データ;
self.postMessage(データ);
}
})
}、1000)
});
pollingWorker.onmessage = function () {
// データをレンダリングする
}
ポーリングワーカー.postMessage('init');
上記のコードでは、ワーカーはデータを毎秒ポーリングし、それをキャッシュと比較します。矛盾している場合は、サーバー側に新しい変更があったことを意味するため、メインスレッドに通知する必要があります。
例: ワーカー 新しいワーカー
ワーカー スレッドは、ワーカー スレッド内に新しいワーカー スレッドを作成することもできます (現在、Firefox ブラウザーでのみサポートされています)。次の例では、計算負荷の高いタスクを 10 個のワーカーに割り当てます。
メインスレッドのコードは次のとおりです。
var ワーカー = 新しいワーカー('worker.js');
worker.onmessage = 関数 (イベント) {
document.getElementById('result').textContent = イベント.データ;
};
ワーカースレッドのコードは次のとおりです。
// ワーカー.js
// 設定
var num_workers = 10;
var items_per_worker = 1000000;
// ワーカーを開始します
var 結果 = 0;
var pending_workers = num_workers;
for (var i = 0; i < num_workers; i += 1) {
var ワーカー = 新しいワーカー('core.js');
worker.postMessage(i * items_per_worker);
worker.postMessage((i + 1) * items_per_worker);
worker.onmessage = ストア結果;
}
// 結果を処理する
関数storeResult(イベント) {
結果 += イベント.データ;
保留中の労働者 -= 1;
if (pending_workers <= 0)
postMessage(result); // 完了しました!
}
上記のコードでは、ワーカー スレッド内に 10 個の新しいワーカー スレッドが作成され、メッセージがこれら 10 個のワーカーに順番に送信され、計算の開始点と終了点が通知されます。計算タスクスクリプトのコードは以下のとおりです。
// コア.js
var 開始;
onmessage = getStart;
関数 getStart(event) {
開始 = イベント.データ;
onmessage = getEnd;
}
var 終了;
関数 getEnd(イベント) {
終了 = イベント.データ;
onmessage = null;
仕事();
}
関数 work() {
var 結果 = 0;
for (var i = 開始; i < 終了; i += 1) {
// ここで複雑な計算を実行します
結果 += 1;
}
postMessage(結果);
近い();
}
API
メインスレッド
ブラウザーはネイティブに Worker()
コンストラクターを提供します。これは、ワーカー スレッドを生成するためにメイン スレッドによって使用されます。
var myWorker = 新しい Worker(jsUrl, options);
Worker()
コンストラクターは 2 つのパラメーターを受け入れることができます。最初のパラメータはスクリプトの URL です (同一生成元ポリシーに準拠する必要があります)。このパラメータは必須であり、JS スクリプトのみをロードできます。それ以外の場合はエラーが報告されます。 2 番目のパラメーターは構成オブジェクトであり、オプションです。その機能の 1 つは、複数のワーカー スレッドを区別するためにワーカーの名前を指定することです。
// メインスレッド
var myWorker = new Worker('worker.js', { name : 'myWorker' });
// ワーカースレッド
self.name // myWorker
Worker()
コンストラクターは、ワーカーを操作するためにメインスレッドによって使用されるワーカー スレッド オブジェクトを返します。 Workerスレッドオブジェクトのプロパティとメソッドは以下のとおりです。
- Worker.onerror: エラーイベントのリスニング関数を指定します。
- Worker.onmessage: メッセージ イベントのリスニング関数を指定します。送信されるデータは
Event.data
プロパティにあります。 - Worker.onmessageerror: messageerror イベントのリスニング関数を指定します。このイベントは、送信されたデータを文字列にシリアル化できない場合にトリガーされます。
- Worker.postMessage(): ワーカー スレッドにメッセージを送信します。
- Worker.terminate(): ワーカー スレッドを直ちに終了します。
ワーカースレッド
Web Worker には独自のグローバル オブジェクトがあります。これはメイン スレッドの「ウィンドウ」ではなく、Worker 用に特別にカスタマイズされたグローバル オブジェクトです。したがって、「window」上で定義されたすべてのオブジェクトやメソッドが使用できるわけではありません。
ワーカー スレッドには、独自のグローバル プロパティとメソッドがいくつかあります。
- self.name: 作業者の名前。このプロパティは読み取り専用であり、コンストラクターによって指定されます。
- self.onmessage:
message
イベントのリスニング機能を指定します。 - self.onmessageerror: messageerror イベントのリスニング関数を指定します。このイベントは、送信されたデータを文字列にシリアル化できない場合にトリガーされます。
- self.close(): ワーカー スレッドを閉じます。
- self.postMessage(): このワーカーを生成したスレッドにメッセージを送信します。
- self.importScripts(): JS スクリプトを読み込みます。
(以上)
作者: wangdoc
アドレス: https://wangdoc.com/
ライセンス: クリエイティブ・コモンズ 3.0