読者です 読者をやめる 読者になる 読者になる

ボク流Javascriptインチキマルチスレッド処理

photo by Beshef

JavaScriptのいいとこ、悪いとこ

JavaScriptはいい。
コンパイルがいらなし、型が無いから、なんとなーく作っていると、なんとなーく動いてくれる。
酒呑みながらいじくるのに最高。

でも、欠点もいっぱいある。
ボクが一番欠点だと感じるのは、スレッド関係が貧弱なこと。
ここ最近になり、WebAPIのWebWorkerがでてきたり、Node.jsがNon BrockingI/Oとかでなんとかなってきているように見える。
でも、WebWorkerは、プリミティブな値しかサブスレッド側に渡せなくて使い物にならないし、Node.jsはサーバーアプリケーションなので、インストールしないとどうにもならない。

特にブラウザで動かすJavaScriptはかなりキッツイ。
処理が終わるまで一切画面が更新されないから、処理がどこまで進んでいるのかを知る術が無いし、バグって無限ループに入っているかもしれないので、いつまでも待ってられない。

インチキしてなんとかしまっせ

そこでキューイング等を使って何とかしようとする。
でも、ヘタに作るとキュー単位に処理を作らないといけなくなり、処理の見通しが悪くなりまくる。
更にはキュー間の値の受け渡しとかも悩ましい。
長年試行錯誤して、今ボクが一番ベターだと思っているのが、クロージャを利用したキューイング処理だ。

処理が細切れなのは仕方が無いが、処理の流れ通りに実装することが出来、さらには値の受け渡しを悩む必要がない。

以下がサンプル。

/** QUEUE **/
var $Q = [];
Function.prototype.queue = function() {
  $Q.push(this);
}

/** TIMER **/
var interval = 0;

function ontimeout() {
  try {
    if ( $Q.length>0 ) {
      $Q.shift()(); // execute
    }
    window.setTimeout(ontimeout, interval);
  } catch (e) {}
}

/** SAMPLE **/
window.onload = function() {
  var elm = {a:0};
  +function() {
    console.log('START!!');
    elm.cmt = 'FINISH!!';
  }.queue();
  for ( var i=0; i<10; i++ ) {
    +function() {
      console.log(elm.a++);
    }.queue();
  }
  +function() {
     console.log(elm.cmt);
  }.queue();
  window.setTimeout(ontimeout, interval);
}

これを実行すると、下記のコンソール出力がされる。

START!!
0
1
2
3
4
5
6
7
8
9
FINISH!!

どう? なかなかわかりやすいでしょ?んん?ぅんん?

ただ、気をつける必要があるのが、クロージャは変数の参照が渡るだけなので、 例えばループのiを使用したいときに、下記のように書くと・・・

/** SAMPLE **/
window.onload = function() {
  for ( var i=0; i<10; i++ ) {
    +function() {
      console.log(i);
    }.queue();
  }
  window.setTimeout(ontimeout, interval);
}
10
10
10
10
10
10
10
10
10
10

となってしまう。 処理は、iのループが終わった後(10になった後)に始まるので、それを参照して出してしまうためだ。 ループ中の値を出すためには・・・

/** SAMPLE **/
window.onload = function() {
  for ( var i=0; i<10; i++ ) {
    +function() {
       var ii = i;
         +function() {
             console.log(ii);
          }.queue();
      }();
  }
  window.setTimeout(ontimeout, interval);
}

とする必要がある。 ループ中に無名関数を実行することで、その時のiの値がiiに保持される。 これで、

0
1
2
3
4
5
6
7
8
9

と出力される。

ここらへんを自然に意識できるようになれば、もう自由自在!

後は、エラー時にキューをクリアするような処理を追加するくらいで、ええのでは。

© 2009-2017 Osajiru All Rights Reserved.