Mochikit.Asyncのサンプルコードを読む

Mochikit.Asyncのドキュメントのサンプルコードを、バラバラにして見ていきます。
以下はMochiKit.Async/META.jsonファイルを読み込みスクリプト自身が使っているMochiKit.Asyncバージョンが最新かどうかをチェックするコードです。

var url = "/src/b/bo/bob/MochiKit.Async/META.json";
/*

    META.json looks something like this:

    {"name": "MochiKit", "version": "0.5"}

*/
var d = loadJSONDoc(url);
var gotMetadata = function (meta) {
    if (MochiKit.Async.VERSION == meta.version) {
        alert("You have the newest MochiKit.Async!");
    } else {
        alert("MochiKit.Async "
            + meta.version
            + " is available, upgrade!");
    }
};
var metadataFetchFailed = function (err) {
  alert("The metadata for MochiKit.Async could not be fetched :(");
};
d.addCallbacks(gotMetadata, metadataFetchFailed);

loadJSONDoc

引数で指定したURLに配置されているJSONファイルをXMLHttpRequestで読み込みます。
戻り値としてDeferredオブジェクトが返されます。Deferredについては後ほど。

MochiKit.Async.VERSION

MochiKit.Asyncのバージョン値です。同様にMochiKit.Async.Nameメンバも存在します。

gotMetadataの引数metaにはloadJSONDoc(url)で取得したMETA.jsonインスタンスが渡されます。ここでは実行している環境でのMochiKit.Asyncバージョンとjsonファイルに記録されているバージョン値を比較し、最新かどうかをチェックしています。

metadataFetchFailedにはloadJSONDocエラー時の処理が書かれています。ここでの処理内容はalertしているだけです。

addCallbacks

DeferredにloadJSONDoc()成功時のコールバック処理gotMeradata()と、失敗時のコールバック処理(MochiKitではerrbackと呼ぶ)metadataFetchFailed()を登録しています。

Deferredとは

MochiKitでは時間のかかる処理、応答待ちする処理のためにコールバック関数を登録して非同期処理を行います。非同期要求の結果が得られたときに実行する一連の関数(この一連の関数のことをコールバックチェインと呼ぶ)と非同期要求エラー時に呼ばれる一連のエラー処理関数(エラーバックチェイン)をDeferredに接続します。MochiKitは非同期要求結果が得られた時点でコールバックチェイン先頭からコールバックを呼び出すか、エラー発生時にはエラーバックを呼び出します。Deferredオブジェクトは各コールバックまたはエラーバックの結果をチェインの次の関数へ渡します。

コールバック/エラーバックチェイン

コールバックシーケンスは内部的にcallback/errbackペアを含む2組のリストとして表現されています。例えば以下のような呼び出しシーケンスは、

var d = new Deferred();
d.addCallback(myCallback);
d.addErrback(myErrback);
d.addBoth(myBoth);
d.addCallbacks(myCallback, myErrback);

次のような内部表現のDeferredに変換されます。

[
    [myCallback, null],
    [null, myErrback],
    [myBoth, myBoth],
    [myCallback, myErrback]
]

Deferredは現在状態の経過も記録します。状態は次の3つの値のどれかになります。

 Value 	Condition
    -1 	no value yet (initial condition)
    0 	success
    1 	error

次のどれかの状態となった場合、Deferedはエラー状態となります。

  1. callbackまたはerrbackに与えられた結果が"instanceof Error"
  2. callbackまたはerrback実行中にエラーを投げた

そうでない場合はDeferredは成功状態となります。Deferredの状態は実行するコールバックシーケンス中の次の要素を決定します。

上記の例のdeferredチェインでcallbackまたはerrbackが発生した場合、次のようなことが起こります(例外が発生し、そのままリターンした状態を想像してください)。

// d.callback(result) or d.errback(result)
if (!(result instanceof Error)) {
    result = myCallback(result);
}
if (result instanceof Error) {
    result = myErrback(result);
}
result = myBoth(result);
if (result instanceof Error) {
    result = myErrback(result);
} else {
    result = myCallback(result);
}

コールバックシーケンスに新たなステップが加えられた場合に結果は保存されます。Deferredが既に値を利用できる場合、新規に加えられたコールバックは即座に呼び出されます。

動作サンプル

Deferredの動作理解のための簡単なサンプルです。MochiKitAJAX Tableデモで使われているdomains.jsonを読み込み、コールバックチェインで値を順番に読み込みます。チェインの動作確認のためにわざとコールバックチェインを3回にわけています。
deferred sample