WebWorkerのデバッグ
WebWorker は便利だけどデバッグしにくい。僕は根っからのプリントデバッガーなんだけど、WebWorker は console.log が使えなくて困る。しかし泣いてばかりもいられない。
WebWorker で console.log を使う
WorkerConsole というやり方がある。下記は簡略版だけど、WebWorker側に console
というグローバル変数を用意して、log
とか error
メソッドで呼び出し元に引数を postMessage
する。そして、呼び出し元の onmessage
でコンソールに表示する。
このやり方には若干問題があって、postMessage
を通過出来るものしか表示できない。つまりオブジェクトを表示して DevTools で属性を確認したりとかできない。
// worker.js global.console = {}; ["log", "debug", "info", "error"].forEach(function(method) { global.console[method] = function() { var args = [].slice.call(arguments).map(function(x) { return x+""; }); postMessage(["console", method].concat(args)); }; }); console.log("ウヒョーー!!");
// app.js window.onmessage = function(e) { var msg = e.data || ""; if (msg[0] === "console") { console[msg[1]].apply(console, msg.slice(2)); } };
WebWorker のかわりに インラインフレーム を使う
WebWorker は postMessage
と onmessage
を駆使してメッセージのやり取りを行うのだけど、インラインフレームでも同じように postMessage
と onmessage
を使ってメッセージのやり取りを行えるので代用できる。以下の例ではクエリストリング中の "debug" の有無で WebWorker か iframe のどちらかのコンテキストで動作するのだけど recvMsg
, sendMsg
という2つの関数をつかって app.js と worker.js 間のやり取りを抽象化している。これだと正真正銘本物の console.log が使えるので、オブジェクトを表示して DevTools で属性を確認し放題になる。
// worker.js var recvMsg, sendMsg; recvMsg = function(msg) { /* メッセージに応じてなんか処理する */ }; if (typeof window === "undefined") { // WebWorker のとき onmessage = function(e) { recvMsg(e.data); }; sendMsg = function() { postMessage([].slice.call(arguments)); }; global.console = {}; ["log", "debug", "info", "error"].forEach(function(method) { global.console[method] = function() { var args = [].slice.call(arguments).map(function(x) { return x+""; }); postMessage(["console", method].concat(args)); }; }); } else { // iframe のとき window.onmessage = function(e) { recvMsg(e.data); }; sendMsg = function() { window.parent.postMessage([].slice.call(arguments), "*") }; }
// app.js var worker, recvMsg, sendMsg; recvMsg = function(msg) { /* メッセージに応じてなんか処理する */ }; var onmessage = function(e) { var msg = e.data || ""; if (msg[0] === "console") { console[msg[1]].apply(console, msg.slice(2)); } else { recvMsg(msg); } }; if (location.search.indexOf("debug") === -1) { // WebWorker を使うとき worker = new Worker("worker.js"); worker.onmessage = onmessage; sendMsg = function() { worker.postMessage([].slice.call(arguments)); }; } else { // ifreme を使うとき worker = document.createElement("iframe"); worker.style.width = worker.style.height = worker.style.borderWidth = 0; worker.onload = function() { worker.contentWindow.document.write('<script src="worker.js"></script>'); window.onmessage = onmessage; sendMsg = function() { worker.contentWindow.postMessage([].slice.call(arguments), "*"); }; }; document.body.appendChild(worker); }