Hatena::ブログ(Diary)

oct inaodu

 | 

2006-12-31

Deferred、結果値の保存、非同期にインターフェースを合わせる


はじめてMochiKit.Async.DeferredのJSON取得のサンプルコードを見たとき、「いつ取得の処理が開始されるのか」、「なぜ、はじめにコールバックを渡さなくてよいのか」という疑問が起きました。

ベルトコンベアで流れ作業を行うときに、作業者が並ぶ前に、材料を投入してラインを始動してしまっているような感覚です。

そのサンプルコードは、以下のようなものです。


var deferred = loadJSONDoc('http://sample.com/data.json');
deferred.addCallback(function(data){
  alert(data); // JSONデータが渡される
});

MochiKit.Async.Deferredの最初のテストコードでは、ハンドラをセットして、コールバックして、その後に再度ハンドラをセットするテストが行われます。


// testEqCallbackは期待値と同じ引数が渡されることをテストする関数
function increment(res){return res + 1;}

var deferred = new Deferred();
deferred.addCallback(testEqCallback(1, "pre-deferred callback"));
deferred.callback(1);
deferred.addCallback(increment);
deferred.addCallback(testEqCallback(2, "post-deferred callback"));

このようにハンドラのセットと、コールバックの順序を前後させることができるのは、Deferredの内部で、リソースやコールバックの返り値を保持しているためです。

Deferredがメッセージキューとして待ち合わせ場所の役割を果たすため、リソースコンシューマーがお互いを待たずに済みます。

リソースの準備完了がいつでもよいように、ハンドラの追加もいつでもよいのです。

同期メッセージが電話だとすると、非同期メッセージ、つまりDeferredは伝言ダイヤルのようなものです。



返り値は、正常終了時の値と異常終了時の値の2つともが保存され、各々コールバックとエラーバックのチェーンの中をバケツリレーのように渡されていきます。



上記の機能を前提とした特徴的な関数にsucceedとfailがあります。

succeed/failは、予め成功または失敗しているDeferredを生成して返す関数です。

テストコードは以下です。


var deferred = succeed(1); // 先に1を引数にコールバックしてしまう
deferred.addCallback(testEqCallback(1, "succeed"));

コールバックが済んでいるDeferredは、addCallbackと同時に追加された関数が実行されます。


succeed/failは、リソースの準備処理が非同期の場合と同期でできる場合が混在する場面で重要になります。

例えば普段は時間がかかる通信のレスポンスが既にキャッシュにあった場合や、プラグインオブジェクトが既に作成されていたり、ウェイトする場所で待たなくてもよかった場合などです。


まず非同期処理の場合は、通常のリソース準備完了の通知の手段としてDeferredを返します。

そして、リソースが既にあり同期処理が行える場合は、succeedでコールバックを済ませてから、Deferredを返します。

この流れを図にしたものが以下です。



Deferredにより、同期処理と非同期処理を同じインターフェースで扱うことができます。

同期と非同期が混在する場面では、非同期にインターフェースを合わせて、シンプルで統一的なコードを書くことができます。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

 |