Edition for Web Developers — Last Updated 11 August 2026
Support in all current engines.
この仕様は、ユーザインタフェースのスクリプトとは独立して、バックグラウンドでスクリプトを実行するためのAPIを定義する。
これにより、クリックまたはその他のユーザー操作に応答するスクリプトによって中断されない長時間実行スクリプトが可能になり、ページの応答性を維持するために譲歩することなく長時間のタスクを実行できる。
ワーカー(ここではこれらのバックグラウンドスクリプトと呼ぶ)は、比較的重いものであり、大量に使用することは想定されていない。たとえば、4メガピクセルの画像の各ピクセルに対して1つのワーカーを起動するのは不適切である。次節の例では、ワーカーの適切な使用法を示している。
一般に、ワーカーは寿命が長く、起動時のパフォーマンスコストが高く、インスタンスごとのメモリコストが高いと予想される。
ワーカーにはさまざまな用途がある。次のサブセクションでは、この用途のさまざまな例を示す。
ワーカーの最も簡単な用法は、ユーザーインターフェイスを中断することなく、計算コストの高いタスクを実行することである。
この例では、メイン文書がワーカーを生成して(単純に)素数を計算し、最近検出された素数を順を追って表示する。
メインページは次のとおりである:
<!DOCTYPE HTML>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< title > Worker example: One-core computation</ title >
</ head >
< body >
< p > The highest prime number discovered so far is: < output id = "result" ></ output ></ p >
< script >
var worker = new Worker( 'worker.js' );
worker. onmessage = function ( event) {
document. getElementById( 'result' ). textContent = event. data;
};
</ script >
</ body >
</ html >
Worker()コンストラクターの呼び出しは、ワーカーを作成し、そのワーカーを表すWorkerオブジェクトを返す。このオブジェクトは、ワーカーとの通信に使用される。そのオブジェクトのonmessageイベントハンドラーによって、コードはワーカーからメッセージを受け取ることができる。
ワーカー自体は次のとおりである:
var n = 1 ;
search: while ( true ) {
n += 1 ;
for ( var i = 2 ; i <= Math. sqrt( n); i += 1 )
if ( n % i == 0 )
continue search;
// found a prime!
postMessage( n);
}
このコードの大部分は、単純に最適化されていない素数の検索である。postMessage()メソッドは、素数が見つかったときにメッセージをページに送り返すために使用される。
これまでの例はすべて、クラシックスクリプトを実行するワーカーを示している。代わりに、ワーカーはモジュールスクリプトを使用してインスタンス化することができる。これには、JavaScript importステートメントを使用して他のモジュールをインポートする機能、デフォルトのstrictモード、ワーカーのグローバルスコープを汚染しないトップレベルの宣言など、通常の利点がある。
importステートメントが使用可能であるため、importScripts()メソッドはモジュールワーカー内で自動的に失敗する。
この例では、メイン文書はワーカーを使用してメインスレッド以外の画像操作を行う。これは、別のモジュールで使用されているフィルターをインポートする。
メインページは次のとおりである:
<!DOCTYPE html>
< html lang = "en" >
< meta charset = "utf-8" >
< title > Worker example: image decoding</ title >
< p >
< label >
Type an image URL to decode
< input type = "url" id = "image-url" list = "image-list" >
< datalist id = "image-list" >
< option value = "https://html.spec.whatwg.org/images/drawImage.png" >
< option value = "https://html.spec.whatwg.org/images/robots.jpeg" >
< option value = "https://html.spec.whatwg.org/images/arcTo2.png" >
</ datalist >
</ label >
</ p >
< p >
< label >
Choose a filter to apply
< select id = "filter" >
< option value = "none" > none</ option >
< option value = "grayscale" > grayscale</ option >
< option value = "brighten" > brighten by 20%</ option >
</ select >
</ label >
</ p >
< div id = "output" ></ div >
< script type = "module" >
const worker = new Worker( "worker.js" , { type: "module" });
worker. onmessage = receiveFromWorker;
const url = document. querySelector( "#image-url" );
const filter = document. querySelector( "#filter" );
const output = document. querySelector( "#output" );
url. oninput = updateImage;
filter. oninput = sendToWorker;
let imageData, context;
function updateImage() {
const img = new Image();
img. src = url. value;
img. onload = () => {
const canvas = document. createElement( "canvas" );
canvas. width = img. width;
canvas. height = img. height;
context = canvas. getContext( "2d" );
context. drawImage( img, 0 , 0 );
imageData = context. getImageData( 0 , 0 , canvas. width, canvas. height);
sendToWorker();
output. replaceChildren( canvas);
};
}
function sendToWorker() {
worker. postMessage({ imageData, filter: filter. value });
}
function receiveFromWorker( e) {
context. putImageData( e. data, 0 , 0 );
}
</ script >
ワーカーファイルは次のようになる:
import * as filters from "./filters.js" ;
self. onmessage = e => {
const { imageData, filter } = e. data;
filters[ filter]( imageData);
self. postMessage( imageData, [ imageData. data. buffer]);
};
ファイルfilters.jsをインポートするもの:
export function none() {}
export function grayscale({ data: d }) {
for ( let i = 0 ; i < d. length; i += 4 ) {
const [ r, g, b] = [ d[ i], d[ i + 1 ], d[ i + 2 ]];
// CIE luminance for the RGB
// The human eye is bad at seeing red and blue, so we de-emphasize them.
d[ i] = d[ i + 1 ] = d[ i + 2 ] = 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
};
export function brighten({ data: d }) {
for ( let i = 0 ; i < d. length; ++ i) {
d[ i] *= 1.2 ;
}
};
Support in all current engines.
この節では、Hello Worldの例を使用して共有ワーカーを紹介する。共有ワーカーは、各ワーカーが複数の接続を持つことができるため、わずかに異なるAPIを使用する。
この最初の例では、ワーカーに接続する方法と、ワーカーが接続したときにページにメッセージを送り返す方法を示す。受信したメッセージはログに表示される。
HTMLページは次のとおり:
<!DOCTYPE HTML>
< html lang = "en" >
< meta charset = "utf-8" >
< title > Shared workers: demo 1</ title >
< pre id = "log" > Log:</ pre >
< script >
var worker = new SharedWorker( 'test.js' );
var log = document. getElementById( 'log' );
worker. port. onmessage = function ( e) { // note: not worker.onmessage!
log. textContent += '\n' + e. data;
}
</ script >
JavaScriptワーカーは次のとおり:
onconnect = function ( e) {
var port = e. ports[ 0 ];
port. postMessage( 'Hello World!' );
}
この2番目の例は最初の例を拡張したもので、2つの点を変更している。1つ目は、イベントハンドラーIDL属性の代わりにaddEventListener()を使用してメッセージを受信すること、2つ目は、メッセージをワーカーに送信して、ワーカーに別のメッセージを送信させることである。受信したメッセージは再びログに表示される。
HTMLページは次のとおり:
<!DOCTYPE HTML>
< html lang = "en" >
< meta charset = "utf-8" >
< title > Shared workers: demo 2</ title >
< pre id = "log" > Log:</ pre >
< script >
var worker = new SharedWorker( 'test.js' );
var log = document. getElementById( 'log' );
worker. port. addEventListener( 'message' , function ( e) {
log. textContent += '\n' + e. data;
}, false );
worker. port. start(); // note: need this when using addEventListener
worker. port. postMessage( 'ping' );
</ script >
JavaScriptワーカーは次のとおり:
onconnect = function ( e) {
var port = e. ports[ 0 ];
port. postMessage( 'Hello World!' );
port. onmessage = function ( e) {
port. postMessage( 'pong' ); // not e.ports[0].postMessage!
// e.target.postMessage('pong'); would work also
}
}
最後に、この例を拡張して、2つのページが同じワーカーに接続する方法を示す。この場合、2番目のページは最初のページのiframe内にあるだけだが、同じ原則が別のトップレベルのトラバース可能内のまったく別のページにも適用される。
次に外部HTMLページを示す:
<!DOCTYPE HTML>
< html lang = "en" >
< meta charset = "utf-8" >
< title > Shared workers: demo 3</ title >
< pre id = "log" > Log:</ pre >
< script >
var worker = new SharedWorker( 'test.js' );
var log = document. getElementById( 'log' );
worker. port. addEventListener( 'message' , function ( e) {
log. textContent += '\n' + e. data;
}, false );
worker. port. start();
worker. port. postMessage( 'ping' );
</ script >
< iframe src = "inner.html" ></ iframe >
次に内部HTMLページを示す:
<!DOCTYPE HTML>
< html lang = "en" >
< meta charset = "utf-8" >
< title > Shared workers: demo 3 inner frame</ title >
< pre id = log > Inner log:</ pre >
< script >
var worker = new SharedWorker( 'test.js' );
var log = document. getElementById( 'log' );
worker. port. onmessage = function ( e) {
log. textContent += '\n' + e. data;
}
</ script >
JavaScriptワーカーは次のとおり:
var count = 0 ;
onconnect = function ( e) {
count += 1 ;
var port = e. ports[ 0 ];
port. postMessage( 'Hello World! You are connection #' + count);
port. onmessage = function ( e) {
port. postMessage( 'pong' );
}
}
この例では、すべて同じマップを表示する複数のウィンドウ(ビューアー)を開くことができる。すべてのウィンドウは同じマップ情報を共有し、1つのワーカーがすべてのビューアーを調整する。各ビューアーは独立して移動できるが、マップ上にデータを設定する場合、すべてのビューアーが更新される。
メインページは興味深くはなく、単にビューアーを開く方法を提供しているだけである:
<!DOCTYPE HTML>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< title > Workers example: Multiviewer</ title >
< script >
function openViewer() {
window. open( 'viewer.html' );
}
</ script >
</ head >
< body >
< p >< button type = button onclick = "openViewer()" > Open a new
viewer</ button ></ p >
< p > Each viewer opens in a new window. You can have as many viewers
as you like, they all view the same data.</ p >
</ body >
</ html >
ビューアーはより複雑になる:
<!DOCTYPE HTML>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< title > Workers example: Multiviewer viewer</ title >
< script >
var worker = new SharedWorker( 'worker.js' , 'core' );
// CONFIGURATION
function configure( event) {
if ( event. data. substr( 0 , 4 ) != 'cfg ' ) return ;
var name = event. data. substr( 4 ). split( ' ' , 1 )[ 0 ];
// update display to mention our name is name
document. getElementsByTagName( 'h1' )[ 0 ]. textContent += ' ' + name;
// no longer need this listener
worker. port. removeEventListener( 'message' , configure, false );
}
worker. port. addEventListener( 'message' , configure, false );
// MAP
function paintMap( event) {
if ( event. data. substr( 0 , 4 ) != 'map ' ) return ;
var data = event. data. substr( 4 ). split( ',' );
// display tiles data[0] .. data[8]
var canvas = document. getElementById( 'map' );
var context = canvas. getContext( '2d' );
for ( var y = 0 ; y < 3 ; y += 1 ) {
for ( var x = 0 ; x < 3 ; x += 1 ) {
var tile = data[ y * 3 + x];
if ( tile == '0' )
context. fillStyle = 'green' ;
else
context. fillStyle = 'maroon' ;
context. fillRect( x * 50 , y * 50 , 50 , 50 );
}
}
}
worker. port. addEventListener( 'message' , paintMap, false );
// PUBLIC CHAT
function updatePublicChat( event) {
if ( event. data. substr( 0 , 4 ) != 'txt ' ) return ;
var name = event. data. substr( 4 ). split( ' ' , 1 )[ 0 ];
var message = event. data. substr( 4 + name. length + 1 );
// display "<name> message" in public chat
var public = document. getElementById( 'public' );
var p = document. createElement( 'p' );
var n = document. createElement( 'button' );
n. textContent = '<' + name + '> ' ;
n. onclick = function () { worker. port. postMessage( 'msg ' + name); };
p. appendChild( n);
var m = document. createElement( 'span' );
m. textContent = message;
p. appendChild( m);
public. appendChild( p);
}
worker. port. addEventListener( 'message' , updatePublicChat, false );
// PRIVATE CHAT
function startPrivateChat( event) {
if ( event. data. substr( 0 , 4 ) != 'msg ' ) return ;
var name = event. data. substr( 4 ). split( ' ' , 1 )[ 0 ];
var port = event. ports[ 0 ];
// display a private chat UI
var ul = document. getElementById( 'private' );
var li = document. createElement( 'li' );
var h3 = document. createElement( 'h3' );
h3. textContent = 'Private chat with ' + name;
li. appendChild( h3);
var div = document. createElement( 'div' );
var addMessage = function ( name, message) {
var p = document. createElement( 'p' );
var n = document. createElement( 'strong' );
n. textContent = '<' + name + '> ' ;
p. appendChild( n);
var t = document. createElement( 'span' );
t. textContent = message;
p. appendChild( t);
div. appendChild( p);
};
port. onmessage = function ( event) {
addMessage( name, event. data);
};
li. appendChild( div);
var form = document. createElement( 'form' );
var p = document. createElement( 'p' );
var input = document. createElement( 'input' );
input. size = 50 ;
p. appendChild( input);
p. appendChild( document. createTextNode( ' ' ));
var button = document. createElement( 'button' );
button. textContent = 'Post' ;
p. appendChild( button);
form. onsubmit = function () {
port. postMessage( input. value);
addMessage( 'me' , input. value);
input. value = '' ;
return false ;
};
form. appendChild( p);
li. appendChild( form);
ul. appendChild( li);
}
worker. port. addEventListener( 'message' , startPrivateChat, false );
worker. port. start();
</ script >
</ head >
< body >
< h1 > Viewer</ h1 >
< h2 > Map</ h2 >
< p >< canvas id = "map" height = 150 width = 150 ></ canvas ></ p >
< p >
< button type = button onclick = "worker.port.postMessage('mov left')" > Left</ button >
< button type = button onclick = "worker.port.postMessage('mov up')" > Up</ button >
< button type = button onclick = "worker.port.postMessage('mov down')" > Down</ button >
< button type = button onclick = "worker.port.postMessage('mov right')" > Right</ button >
< button type = button onclick = "worker.port.postMessage('set 0')" > Set 0</ button >
< button type = button onclick = "worker.port.postMessage('set 1')" > Set 1</ button >
</ p >
< h2 > Public Chat</ h2 >
< div id = "public" ></ div >
< form onsubmit = "worker.port.postMessage('txt ' + message.value); message.value = ''; return false;" >
< p >
< input type = "text" name = "message" size = "50" >
< button > Post</ button >
</ p >
</ form >
< h2 > Private Chat</ h2 >
< ul id = "private" ></ ul >
</ body >
</ html >
ビューアーの作成方法については、注目に値する重要な点がいくつかある。
複数のリスナー。単一のメッセージ処理関数の代わりに、ここでのコードは複数のイベントリスナーをアタッチし、それぞれがメッセージに関連するかどうかを確認するためのクイックチェックを実行する。この例では大きな違いはないが、複数の著者が単一のポートを使用して共同作業し、ワーカーと通信したい場合、単一のイベント処理関数をすべて変更する代わりに、独立したコードを使用できる。
この方法でイベントリスナーを登録すると、この例のconfigure()メソッドで行ったように、使用後に特定のリスナーの登録を解除することもできる。
最後に、ワーカーは:
var nextName = 0 ;
function getNextName() {
// this could use more friendly names
// but for now just return a number
return nextName++ ;
}
var map = [
[ 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 1 , 1 , 0 , 1 , 0 , 1 , 1 ],
[ 0 , 1 , 0 , 1 , 0 , 0 , 0 ],
[ 0 , 1 , 0 , 1 , 0 , 1 , 1 ],
[ 0 , 0 , 0 , 1 , 0 , 0 , 0 ],
[ 1 , 0 , 0 , 1 , 1 , 1 , 1 ],
[ 1 , 1 , 0 , 1 , 1 , 0 , 1 ],
];
function wrapX( x) {
if ( x < 0 ) return wrapX( x + map[ 0 ]. length);
if ( x >= map[ 0 ]. length) return wrapX( x - map[ 0 ]. length);
return x;
}
function wrapY( y) {
if ( y < 0 ) return wrapY( y + map. length);
if ( y >= map[ 0 ]. length) return wrapY( y - map. length);
return y;
}
function wrap( val, min, max) {
if ( val < min)
return val + ( max- min) + 1 ;
if ( val > max)
return val - ( max- min) - 1 ;
return val;
}
function sendMapData( viewer) {
var data = '' ;
for ( var y = viewer. y- 1 ; y <= viewer. y+ 1 ; y += 1 ) {
for ( var x = viewer. x- 1 ; x <= viewer. x+ 1 ; x += 1 ) {
if ( data != '' )
data += ',' ;
data += map[ wrap( y, 0 , map[ 0 ]. length- 1 )][ wrap( x, 0 , map. length- 1 )];
}
}
viewer. port. postMessage( 'map ' + data);
}
var viewers = {};
onconnect = function ( event) {
var name = getNextName();
event. ports[ 0 ]. _data = { port: event. ports[ 0 ], name: name, x: 0 , y: 0 , };
viewers[ name] = event. ports[ 0 ]. _data;
event. ports[ 0 ]. postMessage( 'cfg ' + name);
event. ports[ 0 ]. onmessage = getMessage;
sendMapData( event. ports[ 0 ]. _data);
};
function getMessage( event) {
switch ( event. data. substr( 0 , 4 )) {
case 'mov ' :
var direction = event. data. substr( 4 );
var dx = 0 ;
var dy = 0 ;
switch ( direction) {
case 'up' : dy = - 1 ; break ;
case 'down' : dy = 1 ; break ;
case 'left' : dx = - 1 ; break ;
case 'right' : dx = 1 ; break ;
}
event. target. _data. x = wrapX( event. target. _data. x + dx);
event. target. _data. y = wrapY( event. target. _data. y + dy);
sendMapData( event. target. _data);
break ;
case 'set ' :
var value = event. data. substr( 4 );
map[ event. target. _data. y][ event. target. _data. x] = value;
for ( var viewer in viewers)
sendMapData( viewers[ viewer]);
break ;
case 'txt ' :
var name = event. target. _data. name;
var message = event. data. substr( 4 );
for ( var viewer in viewers)
viewers[ viewer]. port. postMessage( 'txt ' + name + ' ' + message);
break ;
case 'msg ' :
var party1 = event. target. _data;
var party2 = viewers[ event. data. substr( 4 ). split( ' ' , 1 )[ 0 ]];
if ( party2) {
var channel = new MessageChannel();
party1. port. postMessage( 'msg ' + party2. name, [ channel. port1]);
party2. port. postMessage( 'msg ' + party1. name, [ channel. port2]);
}
break ;
}
}
複数ページへの接続 。スクリプトは、onconnectイベントリスナーを使用して、複数の接続をリッスンする。
ダイレクトチャンネル 。ワーカーは、あるビューアーから別のビューアーを指定する"msg"メッセージを受信すると、2つのビューアー間に直接接続を設定し、ワーカーはすべてのメッセージをプロキシすることなく、2つのビューアーが直接通信できるようにする。
マルチコアCPUが普及するにつれて、よりよいパフォーマンスを得る1つの方法は、計算コストの高いタスクを複数のワーカーに分割することである。この例では、1から10,000,000までのすべての数値に対して実行される計算コストの高いタスクが、10のサブワーカーに割り当てられる。
メインページは次のとおりで、結果を報告するだけである:
<!DOCTYPE HTML>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< title > Worker example: Multicore computation</ title >
</ head >
< body >
< p > Result: < output id = "result" ></ output ></ p >
< script >
var worker = new Worker( 'worker.js' );
worker. onmessage = function ( event) {
document. getElementById( 'result' ). textContent = event. data;
};
</ script >
</ body >
</ html >
ワーカー自体は次のとおりである:
// settings
var num_workers = 10 ;
var items_per_worker = 1000000 ;
// start the workers
var result = 0 ;
var pending_workers = num_workers;
for ( var i = 0 ; i < num_workers; i += 1 ) {
var worker = new Worker( 'core.js' );
worker. postMessage( i * items_per_worker);
worker. postMessage(( i+ 1 ) * items_per_worker);
worker. onmessage = storeResult;
}
// handle the results
function storeResult( event) {
result += 1 * event. data;
pending_workers -= 1 ;
if ( pending_workers <= 0 )
postMessage( result); // finished!
}
これは、サブワーカーを開始するループと、すべてのサブワーカーが応答するのを待つハンドラーで構成される。
サブワーカーは次のように実装される:
var start;
onmessage = getStart;
function getStart( event) {
start = 1 * event. data;
onmessage = getEnd;
}
var end;
function getEnd( event) {
end = 1 * event. data;
onmessage = null ;
work();
}
function work() {
var result = 0 ;
for ( var i = start; i < end; i += 1 ) {
// perform some complex calculation here
result += 1 ;
}
postMessage( result);
close();
}
2つのイベントで2つの数値を受け取り、指定された数値の範囲に対して計算を実行し、その結果を親に報告する。
仮に次の3つのタスクを提供する暗号化ライブラリが利用可能になったとする:
ライブラリ自体は次のとおり:
function handleMessage( e) {
if ( e. data == "genkeys" )
genkeys( e. ports[ 0 ]);
else if ( e. data == "encrypt" )
encrypt( e. ports[ 0 ]);
else if ( e. data == "decrypt" )
decrypt( e. ports[ 0 ]);
}
function genkeys( p) {
var keys = _generateKeyPair();
p. postMessage( keys[ 0 ]);
p. postMessage( keys[ 1 ]);
}
function encrypt( p) {
var key, state = 0 ;
p. onmessage = function ( e) {
if ( state == 0 ) {
key = e. data;
state = 1 ;
} else {
p. postMessage( _encrypt( key, e. data));
}
};
}
function decrypt( p) {
var key, state = 0 ;
p. onmessage = function ( e) {
if ( state == 0 ) {
key = e. data;
state = 1 ;
} else {
p. postMessage( _decrypt( key, e. data));
}
};
}
// support being used as a shared worker as well as a dedicated worker
if ( 'onmessage' in this ) // dedicated worker
onmessage = handleMessage;
else // shared worker
onconnect = function ( e) { e. port. onmessage = handleMessage; }
// the "crypto" functions:
function _generateKeyPair() {
return [ Math. random(), Math. random()];
}
function _encrypt( k, s) {
return 'encrypted-' + k + ' ' + s;
}
function _decrypt( k, s) {
return s. substr( s. indexOf( ' ' ) + 1 );
}
ここでの暗号関数は単なるスタブであり、実際の暗号化を行うものではないことに注意する。
このライブラリは次のように使用できる:
<!DOCTYPE HTML>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< title > Worker example: Crypto library</ title >
< script >
const cryptoLib = new Worker( 'libcrypto-v1.js' ); // or could use 'libcrypto-v2.js'
function startConversation( source, message) {
const messageChannel = new MessageChannel();
source. postMessage( message, [ messageChannel. port2]);
return messageChannel. port1;
}
function getKeys() {
let state = 0 ;
startConversation( cryptoLib, "genkeys" ). onmessage = function ( e) {
if ( state === 0 )
document. getElementById( 'public' ). value = e. data;
else if ( state === 1 )
document. getElementById( 'private' ). value = e. data;
state += 1 ;
};
}
function enc() {
const port = startConversation( cryptoLib, "encrypt" );
port. postMessage( document. getElementById( 'public' ). value);
port. postMessage( document. getElementById( 'input' ). value);
port. onmessage = function ( e) {
document. getElementById( 'input' ). value = e. data;
port. close();
};
}
function dec() {
const port = startConversation( cryptoLib, "decrypt" );
port. postMessage( document. getElementById( 'private' ). value);
port. postMessage( document. getElementById( 'input' ). value);
port. onmessage = function ( e) {
document. getElementById( 'input' ). value = e. data;
port. close();
};
}
</ script >
< style >
textarea { display : block ; }
</ style >
</ head >
< body onload = "getKeys()" >
< fieldset >
< legend > Keys</ legend >
< p >< label > Public Key: < textarea id = "public" ></ textarea ></ label ></ p >
< p >< label > Private Key: < textarea id = "private" ></ textarea ></ label ></ p >
</ fieldset >
< p >< label > Input: < textarea id = "input" ></ textarea ></ label ></ p >
< p >< button onclick = "enc()" > Encrypt</ button > < button onclick = "dec()" > Decrypt</ button ></ p >
</ body >
</ html >
ただし、APIの新しいバージョンでは、すべての暗号化作業をサブワーカーにオフロードしたいかもしれない。これは、次のように実行できる:
function handleMessage( e) {
if ( e. data == "genkeys" )
genkeys( e. ports[ 0 ]);
else if ( e. data == "encrypt" )
encrypt( e. ports[ 0 ]);
else if ( e. data == "decrypt" )
decrypt( e. ports[ 0 ]);
}
function genkeys( p) {
var generator = new Worker( 'libcrypto-v2-generator.js' );
generator. postMessage( '' , [ p]);
}
function encrypt( p) {
p. onmessage = function ( e) {
var key = e. data;
var encryptor = new Worker( 'libcrypto-v2-encryptor.js' );
encryptor. postMessage( key, [ p]);
};
}
function encrypt( p) {
p. onmessage = function ( e) {
var key = e. data;
var decryptor = new Worker( 'libcrypto-v2-decryptor.js' );
decryptor. postMessage( key, [ p]);
};
}
// support being used as a shared worker as well as a dedicated worker
if ( 'onmessage' in this ) // dedicated worker
onmessage = handleMessage;
else // shared worker
onconnect = function ( e) { e. ports[ 0 ]. onmessage = handleMessage };
小さなサブワーカーは次のようになる。
キーペアを生成する場合:
onmessage = function ( e) {
var k = _generateKeyPair();
e. ports[ 0 ]. postMessage( k[ 0 ]);
e. ports[ 0 ]. postMessage( k[ 1 ]);
close();
}
function _generateKeyPair() {
return [ Math. random(), Math. random()];
}
暗号化の場合:
onmessage = function ( e) {
var key = e. data;
e. ports[ 0 ]. onmessage = function ( e) {
var s = e. data;
postMessage( _encrypt( key, s));
}
}
function _encrypt( k, s) {
return 'encrypted-' + k + ' ' + s;
}
復号化の場合:
onmessage = function ( e) {
var key = e. data;
e. ports[ 0 ]. onmessage = function ( e) {
var s = e. data;
postMessage( _decrypt( key, s));
}
}
function _decrypt( k, s) {
return s. substr( s. indexOf( ' ' ) + 1 );
}
APIのユーザーは、これが起こっていることさえ知る必要がないことに注意する。APIは変更されていない。ライブラリは、メッセージチャンネルを使用してデータを受け入れている場合でも、APIを変更せずにサブワーカーに委任できる。
ワーカーを作成するには、JavaScriptファイルへのURLが必要である。Worker()コンストラクターは、そのファイルへのURLを唯一の引数として呼び出される。次に、ワーカーが作成されて返される:
var worker = new Worker( 'helper.js' );
ワーカースクリプトをデフォルトのクラシックスクリプトではなく、モジュールスクリプトとして解釈する場合、少し異なるシグネチャーを使用する必要がある:
var worker = new Worker( 'helper.mjs' , { type: "module" });
専用ワーカーは背後でMessagePortオブジェクトを使用するため、構造化データの送信、バイナリデータの転送、他のポートの転送など、すべての同じ機能をサポートする。
専用ワーカーからメッセージを受信するには、WorkerオブジェクトでonmessageイベントハンドラーIDL属性をする:
worker. onmessage = function ( event) { ... };
addEventListener()メソッドを使用することもできる。
専用ワーカーによって使用される暗黙のMessagePortは、作成時にそのポートメッセージキューが暗黙的に有効になるため、WorkerインターフェースにはMessagePortインターフェースのstart()メソッドに相当するものは存在しない。
ワーカーにデータを送信するには、postMessage()メソッドを使用する。構造化データは、この通信チャンネルを介して送信できる。(オブジェクトのクローンを作成するのではなく、オブジェクトを転送することによって、)ArrayBufferオブジェクトを効率的に送信するには、オブジェクトを2番目の引数の配列にリストする。
worker. postMessage({
operation: 'find-edges' ,
input: buffer, // an ArrayBuffer object
threshold: 0.6 ,
}, [ buffer]);
ワーカー内部でメッセージを受信するには、onmessageイベントハンドラーIDL属性を使用する。
onmessage = function ( event) { ... };
この場合も、addEventListener()メソッドを使用できる。
いずれの場合も、データはイベントオブジェクトのdata属性で提供される。
メッセージを送り返すには、やはりpostMessage()を使用する。同じ方法で構造化データをサポートする。
postMessage( event. data. input, [ event. data. input]); // transfer the buffer back
Support in all current engines.
共有ワーカーは、その作成に使用されたスクリプトのURL、オプションで明示的な名前を使用可能、によって識別される。この名前は、特定の共有ワーカーの複数のインスタンスを開始するのを可能にする。
共有ワーカーの範囲は、生成元である。同じ名前を使用する2つの異なるサイトは衝突しない。しかし、ページが同じサイト上の別のページと同じ共有ワーカー名を使用しようとするが、スクリプトURLが異なる場合、失敗する。
共有ワーカーの作成は、SharedWorker()コンストラクターを使用して行われる。このコンストラクターは、最初の引数として使用するスクリプトへのURLと、存在する場合、2番目の引数としてワーカーの名前を受け取る。
var worker = new SharedWorker( 'service.js' );
共有ワーカーとの通信は、明示的なMessagePortオブジェクトを使用して行われる。SharedWorker()コンストラクターによって返されたオブジェクトは、そのport属性のポートへの参照を保持する。
worker. port. onmessage = function ( event) { ... };
worker. port. postMessage( 'some message' );
worker. port. postMessage({ foo: 'structured' , bar: [ 'data' , 'also' , 'possible' ]});
共有ワーカー内では、ワーカーの新しいクライアントがconnectイベントを使用して通知される。新しいクライアントのポートは、イベントオブジェクトのsource属性によって指定される。
onconnect = function ( event) {
var newPort = event. source;
// set up a listener
newPort. onmessage = function ( event) { ... };
// send a message back to the port
newPort. postMessage( 'ready!' ); // can also send structured data, of course
};
この標準では、専用ワーカーと共有ワーカーの2種類のワーカーが定義されている。専用ワーカーは、作成されると作成者にリンクされるが、メッセージポートは、専用ワーカーから他の複数のブラウジングコンテキストまたはワーカーに通信するために使用できない。一方、共有ワーカーは、名前が付けられ、同じ生成元で実行されるスクリプトが作成されると、そのワーカーへの参照を取得して通信できる。Service Workersは第3の種類を定義する。[SW]
グローバルスコープは、ワーカーの"内部"である。
WorkerGlobalScope共通インターフェイスSupport in all current engines.
WorkerGlobalScopeは、DedicatedWorkerGlobalScope、SharedWorkerGlobalScope、ServiceWorkerGlobalScopeなど、特定のタイプのワーカーグローバルスコープオブジェクトの基本クラスとして機能する。
workerGlobal.selfworkerGlobal.locationWorkerLocationオブジェクトを返す。workerGlobal.WorkerNavigatorオブジェクトを返す。workerGlobal.importScripts(...urls)下記は、WorkerGlobalScopeインターフェイスを実装するすべてのオブジェクトによって、イベントハンドラーIDL属性として、サポートされるイベントハンドラー(および対応するイベントハンドラーイベント型)である:
| イベントハンドラー | イベントハンドラーイベント型 |
|---|---|
onerrorSupport in all current engines. Firefox3.5+Safari4+Chrome4+ Opera11.5+Edge79+ Edge (Legacy)12+Internet Explorer10+ Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android? | error |
onlanguagechangeWorkerGlobalScope/languagechange_event Support in all current engines. Firefox74+Safari4+Chrome4+ Opera11.5+Edge79+ Edge (Legacy)?Internet ExplorerNo Firefox Android?Safari iOS5+Chrome Android?WebView Android37+Samsung Internet?Opera Android? | languagechange |
onofflineWorkerGlobalScope/offline_event Firefox29+Safari8+ChromeNo Opera?EdgeNo Edge (Legacy)?Internet ExplorerNo Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android? | offline |
ononlineWorkerGlobalScope/online_event Firefox29+Safari8+ChromeNo Opera?EdgeNo Edge (Legacy)?Internet ExplorerNo Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android? | online |
onrejectionhandled | rejectionhandled |
onunhandledrejection | unhandledrejection |
DedicatedWorkerGlobalScopeインターフェイスSupport in all current engines.
DedicatedWorkerGlobalScopeオブジェクトは、内部ポート(MessagePort)が関連付けられている。このポートは、ワーカーの作成時に設定されるチャンネルの一部であるが、公開されていない。
dedicatedWorkerGlobal.namededicatedWorkerGlobalの名前、すなわちWorkerコンストラクターに指定された値を返す。主にデバッグに役立つ。
dedicatedWorkerGlobal.postMessage(message [, transfer ])dedicatedWorkerGlobal.postMessage(message [, { transfer } ])messageをクローンしてdedicatedWorkerGlobalに関連付けられたWorkerオブジェクトに送信する。transferは、クローンではなく転送されるオブジェクトのリストとして渡すことができる。
dedicatedWorkerGlobal.close()dedicatedWorkerGlobalを中止する。
SharedWorkerGlobalScopeインターフェイスSupport in all current engines.
共有ワーカーは、各接続のSharedWorkerGlobalScopeオブジェクトの connectイベントを通じてメッセージポートを受け取る。
sharedWorkerGlobal.sharedWorkerGlobalの名前、すなわちSharedWorkerコンストラクターに指定された値を返す。同じ名前を再利用することによって、複数のSharedWorkerオブジェクトを同じ共有ワーカー(およびSharedWorkerGlobalScope)に対応付けることができる。
sharedWorkerGlobal.()sharedWorkerGlobalを中止する。
下記は、SharedWorkerGlobalScopeインターフェイスを実装するすべてのオブジェクトによって、イベントハンドラーIDL属性として、サポートされるイベントハンドラー(および対応するイベントハンドラーイベント型)である:
| イベントハンドラー | イベントハンドラーイベント型 |
|---|---|
onconnectSharedWorkerGlobalScope/connect_event Support in all current engines. Firefox29+Safari16+Chrome4+ Opera10.6+Edge79+ Edge (Legacy)?Internet ExplorerNo Firefox Android?Safari iOS16+Chrome Android?WebView Android37+Samsung Internet?Opera Android11+ | connect |
ワーカーイベントループのタスクキューは、タスクとしてイベント、コールバック、およびネットワークアクティビティのみを持つ。
各WorkerGlobalScopeオブジェクトにはclosingフラグがあり、最初はfalseであるが、ワーカーが閉じるように要求されたときにtrueに設定できる。
WorkerGlobalScopeのclosingフラグがtrueに設定されると、イベントループのタスクキューは、それ以降に追加されるタスクを破棄する(キュー上にすでにあるタスクは、特に指定がない限り影響を受けない)。実際には、closingフラグがtrueになると、タイマーの発火が停止し、保留中のすべてのバックグラウンド操作の通知が中断されるなどの動作が行われる。
捕捉されなかったランタイムスクリプトエラーがワーカーのスクリプトの1つで発生するたびごとに、そのエラーが以前のスクリプトエラーの処理中に発生したものでなければ、ユーザーエージェントはワーカーのWorkerGlobalScopeオブジェクトに対してそのエラーを報告する。
WorkerとSharedWorkerとの両方に存在するプロパティ下記は、Worker、WorkerおよびSharedWorkerオブジェクトによってイベントハンドラーIDL属性としてサポートされるイベントハンドラー(および対応するイベントハンドラーイベントタイプ)である:
| イベントハンドラー | イベントハンドラーイベント型 |
|---|---|
onerrorSupport in all current engines. Firefox44+Safari11.1+Chrome40+ Opera?Edge79+ Edge (Legacy)17+Internet ExplorerNo Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android? Support in all current engines. Firefox29+Safari16+Chrome5+ Opera10.6+Edge79+ Edge (Legacy)?Internet ExplorerNo Firefox Android33+Safari iOS16+Chrome AndroidNoWebView Android?Samsung Internet4.0–5.0Opera Android11–14 Support in all current engines. Firefox3.5+Safari4+Chrome4+ Opera10.6+Edge79+ Edge (Legacy)12+Internet Explorer10+ Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+ | error |
WorkerインターフェイスSupport in all current engines.
worker = new Worker(scriptURL [, options ])新しいWorkerオブジェクトを返す。scriptURLがフェッチされてバックグラウンドで実行され、workerが通信チャンネルを表す新しいグローバル環境が作成される。
optionsには、次の値を含めることができる:
nameは、主にデバッグ目的で、そのグローバル環境のnameを定義するために使用できる。
typeを使用して、新しいグローバル環境をscriptURLから値"module"に設定することにより、JavaScriptモジュールとしてロードできる。
credentialsを使用してscriptURL>の取得方法を指定できるが、typeが"module>"に設定されている場合に限られる。
worker.terminate()worker.postMessage(message [, transfer ])worker.postMessage(message [, { transfer } ])messageをクローンし、workerのグローバル環境に送信する。transferは、クローンではなく転送されるオブジェクトのリストとして渡すことができる。
postMessage()メソッドの最初の引数は、構造化データにすることができる:
worker. postMessage({ opcode: 'activate' , device: 1938 , parameters: [ 23 , 102 ]});
SharedWorkerインターフェイスSupport in all current engines.
sharedWorker = new (scriptURL [, name ])新しいSharedWorkerオブジェクトを返す。scriptURLがバックグラウンドでフェッチおよび実行され、sharedWorkerが通信チャンネルを表す新しいグローバル環境が作成される。nameを使用して、そのグローバル環境のnameを定義できる。
sharedWorker = new SharedWorker(scriptURL [, options ])新しいSharedWorkerオブジェクトを返す。scriptURLがバックグラウンドでフェッチおよび実行され、sharedWorkerが通信チャンネルを表す新しいグローバル環境が作成される。
optionsには、次の値を含めることができる:
nameを使用して、そのグローバル環境のnameを定義できる。
typeを使用して、新しいグローバル環境をscriptURLから値"module"に設定することにより、JavaScriptモジュールとしてロードできる。
credentialsを使用してscriptURLの取得方法を指定できるが、typeが"module"に設定されている場合に限られる。
extendedLifetimeを設定して、新しく作成されたグローバル環境に、その所有者がすべてなくなった後でさえも、その操作を実行するための追加の時間を与えるように要求できる。これは、たとえば、ページのアンロード後に非同期作業を実行する場合に便利である。
type、credentials、またはextendedLifetimeの値が同じconstructor URLおよびnameの既存の共有ワーカーの値と一致しないoptionsを使用して共有ワーカーを構築しようとすると、戻されたsharedWorkerによってerrorイベントが発火され、既存の共有ワーカーに接続されないことに注意する。
sharedWorker.グローバル環境との通信に使用できるsharedWorkerのMessagePortオブジェクトを返す。
self.navigator.ユーザーエージェントが利用可能な論理プロセッサーの数を返す。
WorkerNavigatorインターフェイスSupport in all current engines.
WorkerNavigatorインターフェイスは、次のAPIで構成されるNavigatorインターフェイスのサブセットを実装する:
WorkerLocationインターフェイスSupport in all current engines.
Support in all current engines.
WorkerLocationインターフェイスはLocationインターフェイスに似ているが、assign()、replace()、reload()、およびancestorOriginsの各メンバーが存在しない。