2012年8月16日木曜日

HTML5 WebWorkersでjQueryはロードできない

WebWorkersって?

HTML5、厳密にはW3Cが規定するものが本来の仕様だけど、ここでは一般的に注目されているその辺の「新規技術」全般を指すとして、いろいろな仕様追加がされていて、ブラウザベンダさんは皆様お忙しそうですね。

んで、だ。
JavaScriptでクライアントサイドアプリケーションを構築するなんてのが主流?というか流行りなようなわけで。RESTfulでグリグリ動く1ページサイトなんてのが作れたらかっこいいわけですよ。どうせ作るならいろいろ機能も盛り込みたいし、サーバサイドでやってるようなこともクライアント側で出来たらレスポンシビリティの高いインタフェースが作れる。でもユーザの操作を阻害するような重い処理は避けたい。そこで今までになかったバックプロセス、別スレッドでの処理実行があると便利ですよね、ってことでWebWorkersが策定されているわけだ。
若くて誠実な男もいいけど、裕福で落ち着いたナイスミドルも魅力。ってとこですな。バリバリ働く若い男にはUI系ライブラリを使った「見た目」の処理をバンバンやらせて、裏ではナイスミドルがジットリと職人仕事をする、と。

サポートされるスレッド数はブラウザ、バージョンによって異なるみたい。ただ、そもそもCPUのスレッド数を上回る同時実行なんてのはできるわけがないので、並行処理として有効なのは主プロセスも含めてせいぜい3プロセス程度なようです。一般的に普及しているPCの「平均的な」パワーとしてその程度と「言われている」らしい。ま、処理速度を上げるというよりは、別プロセスで実行されることでユーザ操作をロックしないのが目的ですし、おすし。

とりあえず使う。

バックプロセスの主な使い方、といっていいのかわからないけれど、WebWorkersのサンプルを探すとほとんどが大規模な数値計算、例えば3Dモデルの座標計算であったりとか、比較的単純な計算の膨大な繰り返し実行が見つかる。
まあサンプルは誰が見てもわかりやすいものがよいのでそれはそれでよい。使い方がわかれば後は煮るのも焼くのも生でいただくのもこちらの勝手である。

んで使ってみる。

まず裏で動くナイスミドルを別のjsファイルに書く。
back-process.js
onmessage = function(event) {
    // Workerが起動したときの引数はevent.data.*に渡される。形式はJSON。
    var msg = event.data.message;
    var res;
    var status = '';
    /*
      なにかしらの(重たい)処理を書く
    */
    // 仕事が終わったら主プロセスに処理結果を返す。
    postMessage(
        {
            'status': status,
            'result': res
        });
};
元気な若者から呼び出す。
// スレッドオブジェクトを作る。相対パスはHTMLがルート。
var worker = new Worker('js/back-process.js');

// 処理がもどってきたときのフロントの動き
worker.onmessage = function(event) {
    if(event.data.status){
        // jQueryとかつかって処理結果を画面に出す
    }
};
worker.postMessaage({message:'hogehoge'});

よしよし。動く。


バックプロセスで通信がしたい。


じゃあ次。
上のサンプルともいえないようなコードの通り、Ajaxのように非同期でイベント駆動な処理体系でスレッドを管理する。しかもChromeのデベロッパツールなんかでデバッグするとわかるけど、バックプロセスは独自の空間(?)で実行されるため、HTMLで呼び出しているjsのオブジェクトはそのままでは扱えない。しかもDOMやBOMにはアクセスしちゃらめーっってことなので、alert();すらできない。htmlでナイスミドルを呼び出していなくても動くこともあって、主プロセスとは完全に別の話とされているのだろう。
chromeのver21以降のデベロッパーツールではデバッグができるようになったけど、それ以前はフロント側で使わないのにHTMLで呼び出してブレークポイントセットとか必要だったしね。死ね。

それはさておき。
Ajaxぽい非同期実行で管理されるなら、リクエストを定期的に発行する処理をバックプロセスで実行できないか。もしくはユーザトリガーの処理のうちの長い通信を伴うもの、つまりはアップ/ダウンロードなんか、をバックプロセスにお任せしますので終わったら教えてー、みたいなつくりができるといいなと思うわけだ。

じゃあjQueryの$.ajax()でやってみますか!と。
本当はアップロードを作りたいけど、最初はとりあえずPOSTできればなんでもいい。
上記の通り、バックプロセスは独自空間で実行され、HTMLがロードしたjsは全く知らない人なので、とりあえずjQueryをナイスミドルの門下に入れないといけない。
これはimportScripts()でできる。

ナイスミドル門下にjQueryさんを入門させる。
back-process.js
importScripts('jQuery-min.js');
これだけ。
あとは$.ajax()のメソッドを書いて、さっきみたいにWebWorkerを起動すればOK。

ナイスミドルを通信っぽいバックプロセスに書き換える。
とりあえず適当なQueryStringを投げる感じのバックプロセスを作ってみる。 QueryStringはjQueryさんの$.param();でいきましょう。
importScripts('jQuery-min.js');

onmessage = function(event) {
    // Workerが起動したときの引数はevent.data.*に渡される。形式はJSON。
    var msg = event.data.message;
    var res;
    var sts = '';
    $.ajax({
        url:'http://example.com/'
        type:'post',
        data:$.param(msg),
        dataType:'xml',
        async: false,
        timeout:1000,
        success:
        function(response, status){
            sts = status;
            if(sts!='OK') {                
                res = null;
            } else {
                res = response;
            }
        }
    });
    // 主プロセスへ
    postMessage(
        {
            'status': sts,
            'result': res
        });
};
ようしこれでバックプロセスでの通信準備が整った!
まず一発実行して、うまくいったらsetInterval()で定期POSTだ。

颯爽とF5を押してみる。
・・・・?
・・・・あ・・れ・・・・・・?
POSTされない・・・・・だと・・・・・?






WebWorkersでjQueryはロードできない

なにごともトントン拍子で進むわけがないのがプログラミングであり、醍醐味だ。
「オラァ、わくわくしてきたぞ!」と隣のPGには聞こえないようにつぶやきながらF12を押します。
コンソールにはエラーメッセージ
~  DOM Exception
ほぅ。なるほどなるほど、よくわかりません。
DOMにさわっちゃいけないのはWebworkersの仕様。これは守ったぜ俺は。ククク。などと余裕をかましていては意味がないのでどこでエラーになったかを見てみる。
jQueryのwindowオブジェクトを呼び出しているところ。
えっ。えっ?えっ・・・・。


WebWorkersってwindowに触れちゃだめよっていう仕様だったみたい。(ゝω・)テヘペロ☆
調べてみたら既にその道は通ったひとがいたよ。
HTML Web Worker and Jquery Ajax call
回避策としてはXHRで書いてね。ってことだけど、そもそも俺がやりたかったのはアップロードなんだが、XHRでformのファイル投げるのってめんどくさいんですけど・・・?

ということでバックプロセスで通信したい場合に、DOM操作がなくてもjQueryは使えないよ、というお話しでした。ちなみにHTML5で新たに追加されたFormDataオブジェクト。DOMのformのように振舞ってくれるJavascriptオブジェクトなんだけど、こいつもバックプロセス側でnewするとExceptionが吐き出されました。


WebWorkersの使いどころとしては今のところは2つくらいしか考えられない。
  1. 膨大な数値計算。グラフを書いたり、Canvasの描画のための座標計算とか。
  2. ファイル操作。HTML5新機能として注目されているFile APIやFile System APIを利用してI/Oがある場合なんかは別スレッドで実行するべきと考えられる。


とりあえず無駄骨を折ったけど、骨は強くなった。かなぁ・・・。

0 件のコメント:

コメントを投稿