JavaScript/Web Worker API
Web Worker API
[編集]はじめに
[編集]JavaScriptは基本的にシングルスレッドで動作します。これはメインスレッドが重い処理を実行している間、ユーザーインターフェースがフリーズしてしまう可能性があるという問題があります。Web Worker API
はこの問題を解決するための技術で、バックグラウンドでJavaScriptを実行するための標準的な方法を提供します。
Web Workerの基本
[編集]Web Workerは、メインスレッドとは別のバックグラウンドスレッドでJavaScriptコードを実行できるようにします。これにより、複雑な計算や時間のかかる処理をメインスレッドをブロックすることなく実行できます。
- main.js
- (メインスレッド)
// Workerを作成constmyWorker=newWorker('worker.js');// Workerにメッセージを送信myWorker.postMessage({number:1000000});// Workerからのメッセージを受信myWorker.onmessage=function(e){console.log('計算結果: '+e.data.result);};// エラーハンドリングmyWorker.onerror=function(error){console.error('Worker error: '+error.message);};
- worker.js
- (ワーカースレッド)
// メインスレッドからのメッセージを受信self.onmessage=function(e){constnumber=e.data.number;// 時間のかかる計算(例:素数判定)letresult=findPrimes(number);// 結果をメインスレッドに送り返すself.postMessage({result:result});};// 指定した数値までの素数を見つける関数functionfindPrimes(max){constprimes=[];for(leti=2;i<=max;i++){letisPrime=true;for(letj=2;j<=Math.sqrt(i);j++){if(i%j===0){isPrime=false;break;}}if(isPrime)primes.push(i);}returnprimes.length;// 素数の数を返す}
通信の仕組み
[編集]Web Workerとメインスレッド間の通信は、postMessage()
メソッドとonmessage
イベントハンドラを使用して行います。データは値のコピーとして渡され、共有されません。
機能 | メインスレッド | ワーカースレッド |
---|---|---|
メッセージ送信 | worker.postMessage(data) | self.postMessage(data) |
メッセージ受信 | worker.onmessage = function(e) {...} | self.onmessage = function(e) {...} |
エラー処理 | worker.onerror = function(e) {...} | self.onerror = function(e) {...} |
終了 | worker.terminate() | self.close() |
Web Workerの種類
[編集]専用Worker (Dedicated Worker)
[編集]一つのスクリプトからのみ利用可能なワーカーです。最も一般的なタイプのワーカーです。
// メインスクリプトconstdedicatedWorker=newWorker('worker.js');dedicatedWorker.postMessage('Hello from main thread');
共有Worker (Shared Worker)
[編集]複数のスクリプト(ウィンドウ、タブ、フレームなど)から利用可能なワーカーです。
// メインスクリプトconstsharedWorker=newSharedWorker('shared-worker.js');sharedWorker.port.start();// 通信を開始sharedWorker.port.postMessage('Hello from window 1');// shared-worker.jsself.onconnect=function(e){constport=e.ports[0];// 接続してきたポートを取得port.onmessage=function(e){// メッセージを受信し処理port.postMessage('Received: '+e.data);};port.start();// 通信を開始};
Web Workerの制限事項
[編集]Workerでは利用できないAPIがいくつかあります。
利用できないもの | 理由 |
---|---|
DOM操作 (document , window ) | UIスレッドのみが担当 |
親ページの変数・関数へのアクセス | 別スレッドで実行されているため |
alert() , confirm() | ユーザーインタラクションはメインスレッドのみ |
代わりに、Workerでは以下のAPIが利用可能です:
利用可能なAPI | 例 |
---|---|
Web Worker固有API | self , postMessage() |
XMLHttpRequest | ネットワークリクエスト |
WebSocket | 双方向通信 |
タイマー | setTimeout() , setInterval() |
Application Cache | オフラインアクセス |
インポート機能 | importScripts() |
Transferable Objectsによる効率的なデータ転送
[編集]大量のデータを扱う場合は、データのコピーではなく所有権の移転によって効率的に通信できます。
// メインスレッドconstarrayBuffer=newArrayBuffer(1024*1024*32);// 32MB// データの所有権をWorkerに移転worker.postMessage({data:arrayBuffer},[arrayBuffer]);// このあとarrayBufferは使用できなくなる(長さが0になる)console.log(arrayBuffer.byteLength);// 0// Workerスレッドself.onmessage=function(e){constreceivedBuffer=e.data.data;console.log(receivedBuffer.byteLength);// 33554432 (32MB)// 処理後、メインスレッドに所有権を戻すself.postMessage({result:receivedBuffer},[receivedBuffer]);};
Web Workerのユースケース
[編集]Web Workerは以下のような場面で特に有用です:
- 画像・音声・動画処理
- データの解析と加工
- 複雑な計算処理
- 大量のJSON処理
- 暗号化・復号化処理
実用的な例:画像処理
[編集]- main.js
constimageProcessingWorker=newWorker('image-processor.js');constcanvas=document.getElementById('sourceCanvas');constctx=canvas.getContext('2d');constimage=document.getElementById('sourceImage');// 画像をロードimage.onload=function(){canvas.width=image.width;canvas.height=image.height;ctx.drawImage(image,0,0);// キャンバスから画像データを取得constimageData=ctx.getImageData(0,0,canvas.width,canvas.height);// ワーカーに処理を依頼imageProcessingWorker.postMessage({imageData:imageData,filter:'grayscale'},[imageData.data.buffer]);// Transferable Objectsを使用};// 処理結果を受け取るimageProcessingWorker.onmessage=function(e){constresultImageData=e.data.imageData;// 結果を新しいキャンバスに描画constresultCanvas=document.getElementById('resultCanvas');constresultCtx=resultCanvas.getContext('2d');resultCanvas.width=resultImageData.width;resultCanvas.height=resultImageData.height;resultCtx.putImageData(resultImageData,0,0);};// image-processor.js (ワーカー)self.onmessage=function(e){constimageData=e.data.imageData;constfilter=e.data.filter;// フィルタ処理を実行switch(filter){case'grayscale':applyGrayscale(imageData);break;case'invert':applyInvert(imageData);break;// 他のフィルタケース}// 処理結果を返すself.postMessage({imageData:imageData},[imageData.data.buffer]);};// グレースケール変換処理functionapplyGrayscale(imageData){constdata=imageData.data;for(leti=0;i<data.length;i+=4){constavg=(data[i]+data[i+1]+data[i+2])/3;data[i]=avg;// Rdata[i+1]=avg;// Gdata[i+2]=avg;// B// data[i + 3]はアルファ値(透明度)なので変更しない}}// 色反転処理functionapplyInvert(imageData){constdata=imageData.data;for(leti=0;i<data.length;i+=4){data[i]=255-data[i];// Rdata[i+1]=255-data[i+1];// Gdata[i+2]=255-data[i+2];// B}}
パフォーマンス考慮事項
[編集]Web Workerを効果的に使用するためのポイントをまとめます:
- メッセージの送受信にはオーバーヘッドがあります。頻繁に小さなデータを送るよりも、まとめて送る方が効率的です。
- 複雑な計算処理や長時間実行される処理はWorkerに移動しましょう。
- 大きなデータを扱う場合は、Transferable Objectsを使用してパフォーマンスを向上させましょう。
- 処理の進捗状況を定期的にメインスレッドに報告することで、UXを向上させられます。
まとめ
[編集]Web Worker API
は、JavaScriptの単一スレッド制約を克服し、複雑な処理をバックグラウンドで実行することを可能にします。適切に使用することで、レスポンシブなWebアプリケーションを構築できます。メッセージング、共有データ、エラー処理などの機能を理解し、実践的なシナリオで適用することが重要です。
この章で紹介したコード例を元に、実際のプロジェクトでWeb Workerを導入してみてください。特に時間のかかる処理や計算負荷の高い機能がある場合、顕著なパフォーマンス向上が期待できます。