2012-11-30

[Node.js] async を使って複数のファイルを同時に fs.stat する

非同期処理をもっと楽しくする async の async.parallel を使うと複数の処理を並列に行い、全部処理が終わった時点もしくはいずれかの処理でエラーが発生した時点で、代表して一度だけコールバック関数が呼ばれるようにできます。

処理完了もしくはエラー終了の時点で、cb を cb(err, result) の形式で呼び出します。エラーがない場合は err を null とすれば OK です。

var async = require('async'),
    fs = require('fs');

async.parallel([
    function (cb) {
        fs.stat('b.txt', function (err, stat) {
            if (err) { return cb(err); }
            cb(null, +stat.mtime)
        });
    },
    function (cb) {
        fs.stat('b.txt', function (err, stat) {
            if (err) { return cb(err); }
            cb(null, +stat.mtime)
        });
    }
], function (err, results) {
    if (err) { throw err; }
    console.log(results);  // [ 1354174306000, 1354112137000 ]
});

fs.stat するコードが同じことを繰り返している点が格好良くないので一つにまとめます。

var async = require('async'),
    fs = require('fs');

async.parallel(['a.txt', 'b.txt'].map(function (file) {
    return function (cb) {
        fs.stat(file, function (err, stat) {
            if (err) { return cb(err); }
            cb(null, +stat.mtime)
        });
    };
}), function (err, results) {
    if (err) { throw err; }
    console.log(results);  // [1354174306000, 1354112137000 ]
});

async.parallel の第一引数をオブジェクトにすると結果 results もオブジェクトにもできることを用いて、さらに underscore の便利な関数 _.object (二要素の配列の配列もしくは同じ長さの配列2つからオブジェクトを作る関数) を活用して以下のようにできます。

var async = require('async'),
    fs = require('fs'),
    _ = require('underscore');

async.parallel(_.object(['a.txt', 'b.txt'].map(function (file) {
    return [file, function (cb) {
        fs.stat(file, function (err, stat) {
            if (err) { return cb(err); }
            cb(null, +stat.mtime)
        });
    }];
})), function (err, results) {
    if (err) { throw err; }
    console.log(results);  // { 'a.txt': 1354174306000, 'b.txt': 1354112137000 }
});