otsuneさんのtumblr経由で、「MochiKit.Async.Deferredで非同期処理の同期処理を直感的に書く」を見たら、この辺この間のtwitterで書いたスレッドで実現するとどうなるか書いてみたくなったので書いてみる。このエントリと同じく、Flickrの写真をFFFFOUND!とtumblrにポストするスレッドを考えてみます。
まず、予めFlickrのpermalinkとmetadataを取得する「flickr.GetPermalinkThread」と「flickr.GetMetadataThread」、FFFFOUND!とtumblrにポストする「ffffound.PostFFFFOUNDThread」と「tumblr.PostTumblrThread」を作っておきます。これらのスレッドは、中でURLLoaderとか使ってリクエストを投げて、終わったらterminate(スレッドを終了)するようにしておきます。
で、本題のクラス「PostFlickrPhotoThread」は次のような感じになります。
import flickr.GetPermalinkThread;
import flickr.GetMetadataThread;
import ffffound.PostFFFFOUNDThread;
import tumblr.PostTumblrThread;
public class PostFlickrPhotoThread extends Thread
{
private var _getPermalink:GetPermalinkThread;
private var _getMetadata:GetMetadataThread;
protected override function execute():void
{
// スレッド作る
_getPermalink = new GetPermalinkThread();
_getMetadata = new GetMetadataThread();
// スレッド開始
_getPermalink.begin();
_getMetadata.begin();
// スレッド終了を待つ
_getPermalink.join();
_getMetadata.join();
// 次に実行するメソッドをセット
switchExecuteMethod(post);
}
private function post():void
{
// スレッドを作る
// このとき、GetPermalinkとGetMetadataで取得したpermalinkとmetadataを渡してやる
var ffffound:Thread = new PostFFFFOUNDThread(_getPermalink.permalink, _getMetadata.metadata);
var tumblr:Thread = new PostTumblrThread(_getPermalink.permalink, _getMetadata.metadata);
// スレッド開始
ffffound.begin();
tumblr.begin();
// スレッド終了を待つ
ffffound.join();
tumblr.join();
// 次に実行するメソッド(terminateなのでスレッド終了)をセット
switchExecuteMethod(terminate);
}
}
とても直感的で分かり易いと思いませんか?非同期処理なのに、かなり同期処理に近い形でコード書けていると思います。コードを上から順に読んでいけば、処理の流れも一目瞭然です。
非同期処理はスレッドという形で適切にクラス化されているので、再利用性も増しますし、組み合わせの柔軟性も高まります。それでありながら、処理ひとつひとつが嫌になるほどクラス化されているコマンドパターンより書き易いはずです。
ところで、こういう処理って大抵Viewのことなんかはあまり考えられてなくて、いわゆるMVCのControllerがMの操作とVのアニメーションコードでカオスになりがちなのですが、一応その辺もちゃんと考えてあります。例えば、上記のFlickrの写真をポストするという動作の前後に、「投稿中です...というダイアログをフェードさせながら出す」と「そのダイアログをフェードさせながら消す」というViewの操作を入れたいとします。このときは、次のようなControllerを作ります。
public class Controller extends Thread
{
public function Controller(view:View)
{
_view = view;
}
private var _view:View;
protected override function execute():void
{
// 「投稿中です」ダイアログの出現アニメーションを取得
var animation:Thread = _view.getShowProcessingDialogAnimation();
// 開始
animation.begin();
// 終了待ち
animation.join();
// 次
switchExecuteMethod(post);
}
private function post():void
{
var postPhoto:Thread = new PostFlickrPhotoThread();
postPhoto.begin();
postPhoto.join();
switchExecuteMethod(hideDialog);
}
private function hideDialog():void
{
var animation:Thread = _view.getHideProcessingDialogAnimation();
animation.begin();
animation.join();
switchExecuteMethod(terminate);
}
}
PostFlickrPhotoThreadを使って写真をポストするという動作の前後に、アニメーションスレッドを実行するという処理を挟みます。ここでポイントは、アニメーションごとスレッド化してしまい、それをViewから持ってくるところです。このようにしておくと、ダイアログの出現/消失がアニメーションという形で抽象化され、Controllerは実際にどのようなアニメーションをするか、というところから切り離すことが出来ます。アニメーションに関するコードは全てViewに適切にまとめられ、例えば「フェードに加えて拡大縮小のアニメも付けたい」という風になったときでも、Viewの修正だけですみます。もちろん、複数View対応も楽ですし、「やっぱアニメはやめたい」となったときは、何もしないで終了するスレッド(NullThread)を返すようにしてやることで、やはりControllerの修正無く対応することが出来ます。
なんとなくThreadの威力を分かって頂けたでしょうか。
サイト内関連記事
この記事へのトラックバック
トラックバックはありません。
TrackBack URL:
http://www.be-interactive.org/trackback.php?id=304

ただ、非同期処理を、URLLoaderが何らかのErrorEventを送出した等の理由で停止(または処理の分岐)をさせたい場合、DeferredだとaddErrbackで対処できるかと思いますが、このThreadだとどのようになるのでしょうか?
この辺、catchErrorではなく、addTry(function)で例外ハンドラを追加/削除出来るようにした方が良さそうだとか、まだまだ議論の余地はあるのですが。
とりあえず、従来の例外と同じような感覚で処理をすることが出来るようになっています。
なるほどなるほど、その辺りのことはさすが想定内でしたか。
アニメーションやロード周りの基本的なThreadさえ揃ってしまえば、かなり楽しくコーディングできるようになりそうですね。楽しみにしています。
AS3Threadを使わせて頂きました。非同期な処理を素直に書けて、素晴らしいと思います!
ブログのエントリーに書いたので、
ツッコミをお待ちしております!
トラックバックしたかったのですが、
なぜかハジかれてしまったのは内緒です :-P
http://humming.via-kitchen....