Pot.Workeroid

{Pot.Workeroid} new Pot.Workeroid ({Function|String|*} script)

Pot.js と PotLite.js で利用可能。

与えられた script と共に Worker スレッドを生成し、Pot.Workeroid の新しいインスタンスを返します。

Pot.Workeroid の解説については Pot.Workeroid を参照ください。

引数 script には、子 Worker の ファイル名、または function オブジェクト、評価される文字列などを指定します。
コンストラクタの生成と同時に、script が Worker として評価され、実行されます。
実行は常に非同期で行われ、ネイティブ Worker が利用可能な場合 別プロセスで実行されます。
script がファイル名の場合、従来の Worker と同じように実行されます。
この場合、onmessage = function(event) {...} と イベントの設定が必要です。
script が function オブジェクトの場合、その関数を onmessage のイベントとして設定し、評価します。
script が function オブジェクトであり、コード内に onmessage = function(event) {...} の設定がされていた場合
イベントとして設定はされずに評価されます。

また、子 Worker から Pot オブジェクトが利用できます。
Pot.js が実行中でであれば Pot.js が、PotLite.js が実行中の場合は PotLite.js がデフォルトで利用可能です。
したがって、Pot.Deferred.forEach などを利用し CPU を抑えたループが利用できます。
子 Worker 内の Pot オブジェクトは、親スレッドでの Pot オブジェクトとは別のオブジェクトです。

var worker = new Pot.Workeroid(function(data) {
    //                                          
    // このスコープの中が 子 Worker スレッド    
    //                                          
    var add = 1;                                
    postMessage(data + add);                    
});

// メッセージを受信した時
worker.onmessage = function(data) {
    alert(data);
};

// 1 を 子 Worker に送信 => 2 が alert される
worker.postMessage(1);

ファイル名を渡すことに慣れている場合、分かり難いかもしれませんが
コンストラクタに渡した function が、子 Worker の onmessage に設定されます。
つまり、渡した function は別スレッドで動きます。(エミュレート時以外)。

引数の data は、event.data にあたるものです。
function オブジェクトを直接指定した場合、event.data がそのまま 第一引数に返ります。
event オブジェクトを取得したい場合は、第二引数から取得できます。

Pot.globalize() が適応されている場合、new Pot.Workeroid() が new Workeroid() で実行できます。

新しい Pot.Workeroid インスタンスが返ります。

function で指定

script に function オブジェクトを渡した場合の例:

var worker = new Pot.Workeroid(function(data) {
    //                                          
    // このスコープの中が 子 Worker スレッド    
    //                                          
    var add = 1;                                
    postMessage(data + add);                    
    // self.postMessage(...) でも可             
});

// メッセージを受信した時 (addEventListener でも設定可)
worker.addEventListener('message', function(data, event) {
    alert(data);
}, false);

// 1 を 子 Worker に送信 => 2 が alert される
worker.postMessage(1);

ファイル名で指定

script に ファイル名で指定した場合の例:
worker.example.parent.js :

var worker = new Pot.Workeroid('worker.example.child.js');

// メッセージを受信した時 (addEventListener でも設定可)
worker.onmessage = function(data, event) {
    alert(data);
};

// エラー時の設定 (任意)
worker.onerror = function(err) {
    alert(err);
};

// 1 を 子 Worker に送信 => 2 が alert される
worker.postMessage(1);

worker.example.child.js :

// 子 Worker 内

var add = 1;

onmessage = function(event) {
    postMessage(event.data + add);
};

script に function で指定し、その関数内で onmessage の設定を持つ場合:
この場合は ファイル名指定と同じく onmessage 関数に event オブジェクトが直接渡される。

var worker = new Pot.Workeroid(function() {
    //                                          
    // このスコープの中が 子 Worker スレッド    
    //                                          
    var add = 1;                                
    onmessage = function(event) {               
        postMessage(event.data + add);          
    };                                          
});

// メッセージを受信した時
worker.onmessage = function(data, event) {
    alert(data);
};

// 1 を 子 Worker に送信 => 2 が alert される
worker.postMessage(1);

このように ファイル名指定と同じく onmessage の指定と共に実行できますが、
Pot.Workeroid では、onmessage の設定のない function 指定を推奨します。
これは、onmessage の外側のスコープの扱いがブラウザ間で異なる場合があることと、
引数が event なのか data なのか混乱することを避けるためです。

Pot.js を利用する

子 Worker からデフォルトで利用できる Pot オブジェクトを利用すると、
CPU 負荷をかけずに実行することができます。

var worker = new Pot.Workeroid(function(data) {
    //                                               
    // このスコープの中が 子 Worker スレッド         
    //                                               
    var n = 1;                                       
    search: while (true) {                           
        n += 1;                                      
        for (var i = 2; i <= Math.sqrt(n); i += 1) { 
            if (n % i == 0) {                        
                continue search;                     
            }                                        
        }                                            
        // 素数をポスト                              
        postMessage(n);                              
    }                                                
});

// メッセージを受信した時
worker.onmessage = function(data, event) {
    Pot.debug(data);
};

// Worker を開始
worker.postMessage();

この例では、素数をひたすら 親スレッドへポストし続けます。
その間、処理はバックグラウンドで行われますが CPU の配慮がありません。
そこで、Pot.js のイテレータを利用します。

var worker = new Pot.Workeroid(function(data) {
    //                                               
    // このスコープの中が 子 Worker スレッド         
    //                                               
    var n = 1;                                       
    Pot.Deferred.forEver(function() {                
        n += 1;                                      
        for (var i = 2; i <= Math.sqrt(n); i += 1) { 
            if (n % i == 0) {                        
                return;                              
            }                                        
        }                                            
        // 素数をポスト                              
        postMessage(n);                              
    });                                              
});

// メッセージを受信した時
worker.onmessage = function(data, event) {
    Pot.debug(data);
};

// Worker を開始
worker.postMessage();

Pot.Deferred.forEver は、StopIteration が throw されるまでイテレートします。
この変化により、CPU 使用率は減少し安定したスレッドに保てます。
必要に応じて、Pot.Deferred.forEver.slow(...) などの速度調整が可能です。
速度の調整については Deferred 速度の指定 を参照ください。

Pot.Deferred と連携

Pot.Deferred チェイン の中で Pot.Workeroid インスタンスを返すと、
子 Worker が postMessage するまで待機します。

var worker = new Pot.Workeroid(function(data) {
    //                                          
    // このスコープの中が 子 Worker スレッド    
    //                                          
    // タイマーで順序を狂わせるように返す       
    switch (data) {                             
        case 'foo':                             
            setTimeout(function() {             
                postMessage('Foooooooooo!');    
            }, 3000);                           
            break;                              
        case 'bar':                             
            Pot.callLater(2, function() {       
                postMessage('Baaaaaaaaaaaa!!'); 
            });                                 
            break;                              
        case 'baz':                             
            setTimeout(function() {             
                postMessage('Baaaaaaaaaz!!!');  
            }, 20);                             
            break;                              
    }                                           
});

worker.onmessage = function(data) {
    Pot.debug(data);
};

Pot.Deferred.begin(function() {
    worker.postMessage('foo');

    // worker を返すことで
    // 子 Worker が postMessage するまで待機
    return worker;

}).then(function() {
    worker.postMessage('bar');
    return worker;
}).then(function() {
    worker.postMessage('baz');
    return worker;
});
//
// 結果:
//   'Foooooooooo!'
//   'Baaaaaaaaaaaa!!'
//   'Baaaaaaaaaz!!!'
//   の順でコンソールに出力される
//