最近調べたnode.jsのstreamに関連した雑多な内容を、思いつくままに適当に書く。
例えば、
function* createPiGenerator() { var result = 0; for (var i = 1;; i += 4) { yield result * 4; result += 1/i - 1/(i + 2); } }
こんな感じのジェネレータがあったとして、それをファイルに出力したい場合
function createPiStream() { var r = new require('stream').Readable(); r.setEncoding('UTF-8'); // バッファじゃなくてstringとして処理したい var g = createPiGenerator(); // piの計算をするジェネレータ var tid; r._read = function () { // size指定は無視して良い if (!tid) { tid = setInterval(function () { r.push(g.next().value + '\n'); // gは無限に出力できるからチェックは無し }, 50); } }; r.on('end', function () { clearInterval(tid); // 終了時はもうpushできないから消す tid = 0; }); return r; }
こういう風にstreamを作っておくと
createPiStream().pipe(process.stdout);
こんな感じに使えて、この場合は出力するだけだけど、例えばファイルに出力したりとか、そういうのが簡単になる。
ところで、StreamにはobjectModeというのがあって、それを使うと文字列に直さなくても、numberとかobjectとかをそのままデータとしてstreamに流せるようになる。Node.js v0.10.28 Manual & Documentation を参照。
全く別の話で、Gruntみたいなtask runnerで、gulp.jsというのがあって、これはstreamを利用することで、中間ファイルを作らずに、minifyだとか、JSのlintだとか、concatだとの処理を、pipeでつなげて効率的に行うことができて、最近はGruntよりもgulp.jsのほうが簡潔でオシャレで、書きやすいのでおすすめである。
gulp.task('default', function () { gulp.src('src/*.js') .pipe(jshint()) .pipe(uglify()) .pipe(gulp.dest('build')) });
例えばこんな感じにして
$ gulp
とするだけでbuildフォルダ内に処理されたものが作られたり。良いものである。
最近のStreamまわりの話題は、Stream2, Stream3, readableとかで検索するとでてきて、 stream-handbookとか、through2とか、event-streamとかを読むとまあまあ便利さがわかる。
node.jsでの非同期処理はStreamとか、Promiseとか、EventEmitterとか、thunkとか、callbackとかが色々あるけど、きれいに書ける場面がわりと分かれているので、最近の何でもPromiseみたいな風潮に惑わされないようにしたい。 最近おすすめの非同期ライブラリはAsyncで、本格的にはまだ使ってないけど、EventEmitterをベースにした有限オートマトンとか、非同期のロック、バッファ付きのemitterなどがあって、なかなかAPIも簡潔なので、良い。