Pot.js という JavaScript ライブラリを作りました。
1ヶ月半くらい前からこつこつ書いてたんですが
実は先日、HDD トラブルでソースコードまるごと紛失してしまったんです。
作業ディレクトリごとなくなっちゃってもう涙目でした。
システム復元とかキャッシュとかいろいろ探したりがんばって
HDD の断片から数週間前のデータの一部がいちおう見つかって
そのコードの切れ端みたいのと記憶を頼りに元に戻してるところです。
その作業があまりにめんどくさいので、
とりあえず動くところまで元に戻った
非同期処理のオブジェクトのみライブラリにしようと
PotLite.js としてコミットしました。
あまり巨大なライブラリより結局のところよかったのかもしれないです。
それでも、一部の関数など足りてない気がしてならない。
ぜんぜん思い出せないけど。。
PotLite.js
PotLite.js は、
非ブロックでの非同期処理を直列的に書けるようにし、
UI や CPU への負担を軽減するループ処理を中心に実装された JavaScript ライブラリです。
MochiKit ライクな Deferred オブジェクトにより
様々なイテレート (forEach, filter, map, filter, repeat, some など) を可能とします。
※ここで言う MochiKit ライクとは、1 つのチェインが 1 つのインスタンスということです
Download
最新はレポジトリにあります:
ドキュメント/リファレンス
PotLite.js のドキュメントとリファレンス
特徴
- クロスブラウザ + Greasemonkey, Firefox アドオン (XUL), Node.js などで動く
- まるで jQuery のように繋げやすい Deferred チェイン
- Deferred チェイン上で forEach などのイテレートが可能
- チェインのコールバック引数を利用した分割代入 (Destructuring-Assignment) ぽいことが可能
- イテレータは各ループ処理の負荷を計算し自動で負荷軽減する
- 各チェイン間やイテレータで実行速度の指定ができる
- 重たい処理やループを分散させ軽減することが可能
なんだかあまり思いつかなかった。。
重たい処理の軽減というのは、
前に作った
Tombloo の Bookmark パッチ で既に近いことを実装してるんです。
というのも、私はブックマークのタグ名をキーワードとして扱ってるせいか
タグ数が 10000 とか超えてどんどん処理が重くなっちゃうんです。
マシンスペックにもよりますが、
そういった大きな配列やオブジェクトをループするたび重くて
落ちないか心配になってるようじゃどうしようもないなと思って
瞬間的な (もしくは数秒~数十秒間続く持続的な) 負荷をどうにか抑えられないかと
いろいろやってますが、ほとんどは分散させるように実行することと
UI に制御を返すことで、だいぶ安定した動作になったのです。
それを Web ブラウザ上でもできないかと
今回実装したライブラリ Pot.js (PotLite.js) の主な目的がそれです。
あとはやはり、JSDeferred の影響が大きいかもしれないです。
MochiKit や dojo (dojo.Deferred) もそうですが、
「JavaScript の高速化とは、単純に速度だけを目指すのではなく
UI スレッドの最短ブロック時間のほうが重要。」
つまり、無理と高速にしたせいでその処理間、
たとえ一瞬でもカクカクとなるようではイカんでしょという感じでしょうか。
PotLite.js の非同期イテレータは、
適度に使い分けることで UI スレッドを独占するような処理の
負荷分散的なツールとして使えると思います。
まだまだ改善の余地はあるのですが、
とりあえず安定して動くかたちになったので
ここで記事も書いておこうと思ったしだいです。
(というか最近ぜんぜんブログ書いてなかった…)
サンプルコード
サンプル的なコードを書いてみます。
ぜんっぜんいい感じの例が思いつかなくて苦しんでるのですが、
サンプルコード書く専用のプログラマがいてもいいんじゃないかしら。。
Pot.Deferred を使って非同期処理を直列化する例です。
まず、Deferred を使わないで書いた場合、
var elem = document.getElementById('output');
// 5 秒後に foo を出力
setTimeout(function() {
elem.value = 'foo';
// さらに 4 秒後に bar を付け足す
setTimeout(function() {
elem.value += 'bar';
// そしてさらに 3 秒後に baz を付ける
setTimeout(function() {
elem.value += 'baz';
}, 3000);
}, 4000);
}, 5000);
上の例は、ネストがどんどん深くなってしまっています。
これを Deferred (Pot.Deferred) を使って書くと、
var elem = document.getElementById('output');
wait(5).then(function() {
elem.value = 'foo';
}).wait(4).then(function() {
elem.value += 'bar';
}).wait(3).then(function() {
elem.value += 'baz';
});
このように書くことができます。
非同期処理を直列的に書けます。
本当はもっと複雑なことに向いてる気もするのですが、
例が思いつかない。。困りました。
というわけで、 PotLite.js でした!
レポジトリ