複数ファイルを同時ダウンロードして順番に書き出す

@yssk22 さんの node.js ハンズオン資料に「複数ファイルを同時ダウンロードして順番に書き出す」というサンプルがあります。
http://dl.dropbox.com/u/219436/node.js/handson/build/html/intro/async_io.html#id8


今回は、このサンプルを拝借して、同等の処理をnueを使って書いたらどんなコードになるか試してみました。


変更点は次の通り

  • モジュールにnueを追加し、flowとas関数をvarで定義。
  • download関数のシグニチャを修正。indexの代わりにcallbackをとるように変更。
  • download関数の中でnotifyDone関数を呼び出している箇所を、callbackを呼び出すように変更。
  • allDone関数で使っているdownloads変数をパラメータで渡すように変更。
  • notifyDone関数を削除。
  • mainFlow関数を追加し、download関数とallDown関数の呼び出しをflowの中で実施。


こんな感じになりました。

// モジュール読み込み
var util = require('util');
var url = require('url');
var http = require('http');
var flow = require('nue').flow;
var as = require('nue').as;

function download(urlStr, callback){
  var u = url.parse(urlStr);
  var client = http.createClient(u.port || 80, u.hostname);
  var request = client.request('GET',
    u.pathname,
    {
      host: u.hostname
    });
  request.end(); // リクエストの送信終了
  // response イベントの非同期処理
  request.on('response', function(response){
    // データ取得イベントの非同期処理
    var buff = '';
    response.setEncoding('utf8');
    response.on('data', function(chunk){
      // chunk は受信したデータ (デフォルトでUTF8 エンコード)
      buff += chunk;
    });
    // レスポンス終了イベントの非同期処理
    response.on('end', function(){
      callback(null, response, buff);
    });
  });
}

// 全部の処理が終わったら実行される
function allDone(downloads){
  for(var i in downloads){
    var r = downloads[i];
    console.log("---- [" + i + "] ");
    console.log(r.response.statusCode);
    for(var j in r.response.headers){
      console.log(j + ": " + r.response.headers[j]);
    }
    console.log('');
    util.print(r.body);
    console.log('');
  }
}

// nueを使ったフロー制御
var mainFlow = flow('main')(
  function startDownload(urls) {
    this.asyncEach(urls, function (url, group) {
      download(url, group.async({response: as(1), body: as(2)}));
    });    
  },
  allDone,
  function end() {
    if (this.err) throw this.err;
  }
);

var urls = process.argv.slice(2);
mainFlow(urls);


こんな感じで実行できます。

node donwload.js http://www.google.co.jp/ http://nodejs.org/


書いてみるまで気づかなかったのですが、主要な処理を行うdownloadとallDoneの両関数をnueに依存させる必要がなかったというのがナイスなポイントです。
非同期な処理を行う関数(download)は、やはりcallbackを受け取るようすることでnueからも呼び出しやすくなりますね。


主要な関数はnueに依存させずに書く、しかもflowの外に書く。非同期処理はcallbackを受け取るようにする。関数は粗結合にする。間をnueでつなぐ。というのが理想かな?