2011年11月23日

JavaScript非同期ライブラリ PotLite.js 1.22 リリース。アスペクト指向っぽく書けるSignal実装

追記

PotLite.js 1.22 はバギーなため 記事内のリンクを 1.23 に変更しました。

PotLite.js 1.22 リリースしました。

PotLite.js は、非同期処理や負荷のかからないループ処理やイテレータを重視した JavaScript ライブラリです。
経緯などについては、以前の記事 や、 CPU 使用率のベンチマーク結果の記事 を参照ください。

ダウンロード

PotLite.js 最新

レポジトリ


PotLite.js バージョン 1.22 では Pot.Signal オブジェクトを実装しました。

Pot.Signal は、任意のハンドラを登録し シグナルを送信して実行に移すことが可能です。
言い方を変えると、自らイベントを作成し、任意のタイミングで呼び出せるようなものです。

「イベント」という概念で実装したわけではありませんが、ここでは便宜上イベントという言い方をします。

Pot.Signal には大まかに以下の関数があります。

  • attach : イベントを登録
  • detach : イベントを解除
  • signal : イベントを実行

Pot.Signal の使用例

Pot.Signal はいろいろな使い方があります。
例えば、
// イベントを登録
//  (第一引数に文字列を渡すと document.getElementById を使用する)
var handler = attach('#foo', 'click', function(ev) {...});

// イベント解除
detach(handler);
上のように addEventListener/removeEventListener の代わりに使用したり、
var MyObj = {};

// 独自のシグナルを登録
var handler = attach(MyObj, 'clear-data', function() {
  // プロパティの初期化など
  MyObj.data = null;
  MyObj.time = null;
});

attach(window, 'load', function() {
  // 初期化するシグナルを送信
  signal(MyObj, 'clear-data');

  // リセットボタンを押した時にもクリアするよう設定
  attach('#reset', 'click', function() {
    signal(MyObj, 'clear-data');
  });

  // 既存の処理など
  myLoadProcess();
  //...
});
上のように attach, signal を独自のシグナルとして使用したりできます。

そして、attachBefore と attachAfter を使うと処理の前後に何らかの動作を追加できます。
// 保存ボタンを押した時のイベントを設定
attach('#saveData', 'click', function() {
  // 保存する関数
  saveData(document.getElementById('inputText').value);
  // ユーザーに保存を伝える関数
  showSaveData('Saved!');
});

// これにフォーカス移動するよう後付けする
attachAfter('#saveData', 'click', function() {
  document.getElementById('inputText').focus();
});

// その前にログを取るよう設定する
attachBefore('#saveData', 'click', function() {
  MyLogger.log('Save inputText');
});
attachBefore, attachAfter はいくつでも登録できます。
登録した順に実行されます。

また、attachPropBefore, attachPropAfter という関数もあります。
これは、attachBefore, attachAfter と違い、
オブジェクトの関数にダイレクトに作用します。
オブジェクトの持つ関数が呼ばれると、自動的に Before, After のハンドラ関数が実行されます。


例えば何らかのアプリケーションを実行する時に、ログをとりたくなった場合。
var MyApp = {
  execute : function() {
    // 何らかの処理を開始する
    myAppDoit();
  }
  // ...
};

attach('#execute', 'click', function() {
  // アプリケーションを実行
  MyApp.execute();
});

// 実行する前にログを取る
attachPropBefore(MyApp, 'execute', function() {
  MyLogger.log('Begin execute');
});

// 実行した後のログを取る
attachPropAfter(MyApp, 'execute', function() {
  MyLogger.log('End execute');
});
このように設定することで、上の例の場合では
MyApp.execute が実行される前後に任意の処理を追加できます。

解除したくなったら、
var handler = attach(...);

// シグナルを解除
detach(handler);
attach*() 関数の戻り値のハンドラオブジェクトを使い、
detach (Pot.Signal.detach) で解除できます。

他にも detachAll() を使い一括で解除することもできます。


Pot.attach(), Pot.signal() などは Pot.Signal.attach() として実装されていますが、
Pot オブジェクトからも参照できます。
Pot.globalize() を実行しておくと、単に attach(obj, ...) と書けます。
今回の例では attach と表現しています。


これらの関数、とくに Before, After を使うことで、
アスペクト指向 (AOP) っぽいプログラミングが可能となります。

Pot.Signal は Node.js などのサーバでの利用も可能ですが、
Web ブラウザ上、HTML5 での Web Storage (localStorage や sessionStorage) を使う時などに便利と思います。

また、attach 系の関数 (attach*) の後に .once を付けると、一度だけ実行されます。
// 一度クリックしたら解除される
attach.once('#hoge', 'click', function() {...});

Pot.Signal の関数

Pot.Signal.attach(object, signalName, callback[, useCapture])
スロットにシグナルを登録します。 Pot.Signal が保持しているハンドラリストに任意のオブジェクトとコールバック関数を登録します。 引数 object が DOM エレメントだった場合、addEventListener と同じように働きます。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.attachBefore(object, signalName, callback[, useCapture])
スロットに存在するハンドラが呼ばれる前に実行されるシグナルを登録します。 Pot.Signal.attach() で登録したものと同じシグナル signalName で登録した場合、 attach() のハンドラが呼ばれる前に実行されます。 同じシグナルが存在しない場合 signal() によって実行しても何も起きません。 DOM エレメントに対して attach() した場合も同様です。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.attachAfter(object, signalName, callback[, useCapture])
スロットに存在するハンドラが呼ばれた後に実行されるシグナルを登録します。 Pot.Signal.attach() で登録したものと同じシグナル signalName で登録した場合、 attach() のハンドラが呼ばれた後に実行されます。 同じシグナルが存在しない場合 signal() によって実行しても何も起きません。 DOM エレメントに対して attach() した場合も同様です。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.attachPropBefore(object, propName, callback)
object が持つ関数 propName が呼ばれる前に実行されるシグナル callback を登録します。 attachBefore() と違い、Pot.Signal.signal() を呼ばなくても object[propName] が呼ばれた時に自動的に実行されます。 attachPropBefore は、DOM オブジェクトに対しての動作は保証されません。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.attachPropAfter(object, propName, callback)
object が持つ関数 propName が呼ばれた後に実行されるシグナル callback を登録します。 attachAfter() と違い、Pot.Signal.signal() を呼ばなくても object[propName] が呼ばれた時に自動的に実行されます。 attachPropAfter は、DOM オブジェクトに対しての動作は保証されません。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.detach(object[, signalName[, callback[, useCapture]]])
Pot.Signal.attach*() によって登録したシグナルを 1 つ解除します。 attachBefore, attachAfter, attachPropBefore, attachPropAfter で登録したものも同様に解除可能です。 引数に object だけを渡した場合、関連するシグナルが 1 つ解除されます。 引数 signalName, callback を渡すと明確に解除できます。 また、attach() などの戻り値のハンドラを object として渡して解除もできます。 登録と解除の関係は、setTimeout と clearTimeout の関係と同様に扱えます。 DOM エレメントに対しては、 removeEventListener と同様に扱うことができます。 解除成功時に true、失敗時に false が返ります。
Pot.Signal.detachAll([object[, ...signals]])
Pot.Signal.attach*() によって登録したシグナルをすべて解除します。 attachBefore, attachAfter, attachPropBefore, attachPropAfter で登録したものも同様に解除可能です。 引数に object だけを渡した場合、関連するシグナルがすべて解除されます。 引数 signals を渡すと一致するシグナルがすべて解除できます。 シグナル名となる signals は文字列または配列で複数指定できます。
Pot.Signal.signal(object, signalName[, ...args])
Pot.Signal.attach() によって登録したシグナルを実行します。 引数 object と signalName に一致するシグナルがすべて実行されます。 実行される関数には第三引数~以降に任意の引数が渡せます。

PotLite.js

以下は PotLite.js について情報です。
インストールや概要など、不要な場合は読み飛ばしてください。

PotLite.js ダウンロード

最新

レポジトリ

$ git clone git://github.com/polygonplanet/Pot.js

GitHub : polygonplanet/Pot.js

動作環境

以下の Web ブラウザで動作確認済みです。

  • Mozilla Firefox *
  • Internet Explorer 6+
  • Safari *
  • Opera *
  • Google Chrome *

また、以下の環境でも動作するよう設計されています。

  • Greasemonkey (userscript)
  • Mozilla Firefox Add-On (on XUL)
  • Node.js
  • Other non-browser environment

インストール

一般的な方法で動作します。

例:

<script type="text/javascript" src="potlite.min.js"></script>
<!--または-->
<script type="text/javascript" src="http://api.polygonpla.net/js/pot/potlite/1.23/potlite.min.js"></script>

Node.js の場合。

// Example to define Pot object on Node.js.
var Pot = require('./potlite.min.js');
Pot.debug(Pot.VERSION);

Pot.Deferred.begin(function() {
  Pot.debug('Hello Deferred!');
}).then(function() {
  // ...
});
// ...

Greasemonkey (userscript) の例。

// ==UserScript==
// ...
// @require  https://github.com/polygonplanet/Pot.js/raw/master/potlite.min.js
// ...
// ==/UserScript==
Pot.Deferred.begin(function() {
  return Pot.request('http://www.example.com/data.json').then(function(res) {
    return Pot.parseFromJSON(res.responseText);
  });
}).then(function(res) {
  Pot.debug(res);
  // do something...
});
//...
PotLite.js をバージョンを限定して Web から直接読み込みたい場合、
上の GitHub リンクでは常に最新になってしまうため
実装の差異による不具合が発生するかもしれません。
そのため、API サーバを用意しました。
これは、1.23 の部分をリリース済みのバージョンに合わせて変更できます。
レポジトリに (例えば 1.xx と) バージョンをタグ付けした時に、
あわせて API サーバに置くようにしています。

例えば Greasemonkey で 1.23 を使いたい場合、
// ==UserScript==
// ...
// @require  http://api.polygonpla.net/js/pot/potlite/1.23/potlite.min.js
// ...
// ==/UserScript==
と記述できます。

Greasemonkey に限らず script タグからでもなんでも自由に使ってください。



jQuery プラグインとしての例:

// jQuery を読み込んだ後に実行。
Pot.deferrizejQueryAjax();

// Ajax 系の関数が Pot.Deferred を返すようになる
$.getJSON('/hoge.json').then(function(data) {
  alert(data.results[0].text);
}).rescue(function(err) {
  alert('Error! ' + err);
}).ensure(function() {
  return someNextProcess();
});

// エフェクトなどを Deferred 化する 'deferred' が追加される
$('div#hoge').deferred('hide', 'slow').then(function() {
  // ( hide() が終了したあとの処理)
});

Pot.deferrizejQueryAjax() は現状、
ライブラリ側で実行しません。
なので、プラグインを使用する場合は
コードの最初などで実行する必要があります。

リファレンス・マニュアル

より詳しい情報はすべてリファレンスに載っています。
基本的な導入や、各メソッド・関数についても扱っています。

動作テスト

以下のページで動作テストができます。
ページを開くと実装されている主な関数・メソッドを全てテストします。

自動生成されたドキュメント

Closure Compiler によりソースコードから自動生成されたドキュメントです。

殆どの関数ごとにサンプルコードを載せているので、ある程度は参考になると思います。
生成物をすべて確認しているわけではないので、誤認識してる箇所もあるかもしれません。
より詳細な実装などは直接ソースコードを参照ください。




不明な点、要望やバグや感想などありましたら
@polygon_planet や 下のレポジトリから、またはメールでなんでもどぞです。

レポジトリ

0 件のコメント:

コメントを投稿