node.jsを使ったチャットアプリケーションとnode-seqの紹介

2012年3月20日追記

node-seqを編集したので、記事を書きました。
catchを使いやすくしています。
node-seq編集 - wktkWebDiary

ユーザのFacebook友達情報を引き継いで複数人とチャットが出来るアプリケショーンをnode.jsの勉強がてら作ってみました。

https://github.com/YaaaaaSuuuuu/FaceRoom


ログインのたびにユーザのフレンドリストを取得しているので、ログインの動作がかなり重くなっています。ホントならフレンドリストはcronで回してログイン時はユーザの認証だけにしたかった。。。

インストールの仕方とかはREADME.mdに書いたので、そこらへんを読めば多分動きます。
後ろでMongoDBとRedisが動いていないといけないのですが、どちらも簡単にインストールできるので、つまずくことはないと思います。
Facebookの友達情報とFaceRoomのユーザと照会をするので、テストユーザを最低2人作ってそれぞれ友達にしておいて下さい。

プログラム自体は特に難しい所とかなかったので、2週間毎日1時間くらいの時間でサクサク出来ました。
ただ、きちんとテストコードを書いていないので、そこが今後の課題です。

デブサミに参加して教えてもらったnode-seqというモジュールがすごく便利で、このアプリでも主要箇所に利用しています。
node.jsはコールバック関数のネストが問題になって、例えばDBから取得したデータを元にさらにDBから情報を取得するなんていう処理を行うと普通なら

Collection.findOne(query, function (error, docs) {
   var query_id = doc._id;
    AnotherCollection.findOne({doc_id: query_id}, function (err, target) {
           // something you want to do
    });
});

みたいにコールバック関数のネストが続きます。
まぁ上記の例はきちんとスキーマを設定してあげる事で解決できたりすることもあると思います。

そこでGitHub - substack/node-seq: Chainable asynchronous flow control for node.js with sequential and parallel primitives and pipeline-style error handlingの出番です。
このモジュールを使うとコールバックのネストが次のようにすっきりします。

var Seq = require('seq');
Seq()
      .seq(function () {
             Collection.findOne(query, this);
       })
       .seq(function (doc) {
              var query_id = doc._id;
              AnotherCollection.findOne({doc_id: query_id}, this)
       })
       .seq(function (target) {
              // something you want to do
       })
      ;

各seq実行時のエラーは

Seq()
       .seq(function () {
            // foo
        })
       .catch(function (err) {
            // bar             
        })
        .seq(function (arg){
            // foobar
        })
       ;

のようにキャッチできます。
node-seqは必ず第一引数がエラー値になるっていうのが大事です。
なので、自分でライブラリを作る場合は引数のコールバック関数の第一引数にエラー値を渡すっていう処理が必要です。

var Seq = require('seq');

function lib (arg, callback) {
  var error = null;
  if (!arg) {
    error =  new TypeError('arg is necessary');
  }
  callback(error, arg);
}

Seq()
   .seq(lib, undefined, Seq)
   .catch(function (error) {
      console.error(error);  // TypeError
   })
   .seq(function (arg) {
      console.log(arg);
   })
  ;

のようになります。
ただこのままだとエラーをcatchしたあとに続く処理も行われてしまうため、一工夫する必要みたいです。※改良中

こんな感じでnode-seqを使うとコードの見通しが良くなるし、自分でライブラリを作るときにも利用できるのでお勧めです。

来週末のNode.js4時限目がすごく楽しみです。