さんの記事 。
ナイスです! なんと素晴らしいタイミングでのブログでしょうか!「」で さんのプレゼンで node-v0.7.8 から isaacs 版 Domain が導入されるという発表がありましたが、予定通り昨日 Domain 機能付きの node-v0.7.8 がリリースされました。 しかもDomain のドキュメント付きです。 ちょうど さんの記事の例は node.js の新機能 Domain を教科書通りに適応するとどうなるのか紹介するのにぴったりのお題なので Node.js v0.8 の新機能 Domain の使い方 について書かせていただきます。Node.jsの新機能 Domain を使う
まずドメインを利用してエラー処理するには
- domain モジュールの require()
- domain オブジェクトの生成
- domain で受けるエラーリスナーの登録
- domain とエラーハンドリングとの結び付け
- eventEmitter の場合は Domain.add
- 関数の場合は Domain.bind
といった段取りが必要です。
さんの記事にあるコードを、Domain 使ったらどのようなコーディングパターンが考えられるかいくつ例示してみたいと思います。。(注:動作には node-v0.7.8 以降が必要です。)今回3つのパターンで考えました。- パターン1: コールバック関数にドメインを結びつけてエラーハンドリングを行う
- パターン2: 関数呼び出し時にドメインを結びつけてエラーハンドリングを行う
- パターン3: 関数定義時にドメインを結びつけてエラーハンドリングを行う
ではサンプルコードとともにドメインを使ったコーディングパターンを例示していきます。
パターン1: コールバック関数にドメインを結びつける方法
まずはエラーオブジェクトを含むコールバックでドメインとエラーを結びつける場合です。
// domain_sample1.jsvar domain = require('domain');var fs = require('fs');var d = domain.create();function countChars(filename, callback) { // コールバックに直接ドメインをバインド fs.readFile(filename, 'utf-8', d.bind(function(err, data) { if (err) throw err; callback(data.length); }));}function main(args) { countChars(args[0], function(length) { console.log(length); });}d.on('error', function(err) { console.log(err.message);});main(process.argv.slice(2))
unixjp:~/tmp/github/node> ./node domain_sample1.js hogeENOENT, open 'hoge'
はい、きれいなコードになりましたね。
パターン2: 関数呼び出しに時にドメインを結びつける方法
関数を呼び出すときにバインドしたい場合は、
// domain_sample2.jsvar fs = require('fs');var domain = require('domain');var d = domain.create();function countChars(filename, callback) { fs.readFile(filename, 'utf-8', function(err, data) { if (err) throw err; callback(data.length); });}function main(args) { // 関数を呼び出す時にドメインをバインドする。 (d.bind(countChars))(args[0], function(length) { console.log(length); });}d.on('error', function(err) { console.log(err.message);});main(process.argv.slice(2));
な感じです。
パターン3: 関数定義にドメインを結びつける方法
関数定義直後にバインドするなら、
// domain_sample3.jsvar fs = require('fs');var domain = require('domain');var d = domain.create();function countChars(filename, callback) { fs.readFile(filename, 'utf-8', function(err, data) { if (err) throw err; callback(data.length); });}// 関数定義直後にドメインにバインドcountChars = d.bind(countChars);function main(args) { countChars(args[0], function(length) { console.log(length); });}d.on('error', function(err) { console.log(err.message);});main(process.argv.slice(2));
といったようになります。
まとめ
他には event.EventEmitter のエラーと結び付けたい場合は Domain.bind() ではなく Domain.add() すればドメイン内のエラー処理で扱えます。
このように Domain の機能を使うとこれまでと違った形でエラーハンドリングをする可能性が広がり、従来より可読性の高く・簡潔なコードが書けるようになるでしょう。追記: Domain.intercept() を使う
さんからのコメントにあるよう domain.bind(cb, true) だと throw しなくてもエラーをひっかけてくれます。(呼ばれる関数の第一引数が Error インスタンスであることが条件ですけど) これをまとめて domain.intercept() という関数になってます。
ということで domain.intercept() を使うともっとすっきりに書けます。// domain_sample4.jsvar domain = require('domain');var fs = require('fs');var d = domain.create();function countChars(filename, callback) { fs.readFile(filename, 'utf-8', d.intercept(function(err, data) { callback(data.length); }));}function main(args) { countChars(args[0], function(length) { console.log(length); });}d.on('error', function(err) { console.log(err.message);});main(process.argv.slice(2));
unixjp:~/tmp/github/node> ./node domain_sample4.js hogeENOENT, open 'hoge'
ありがとうございます。 > さん。
追記の追記: Domain.run() を使う。
またまた さんから twitter 経由で Domain.run() の使い方を教えていただきました。(実はマニュアルには記載されていませんけど)→ さんからのコメントで public API としてドキュメント化されました。
これを使うと上記パターン2(関数呼び出しに時にドメインを結びつける方法)のコードで (d.bind(fn))(args) と書いていたところが d.run(function() {fn(args)} と書けて、もっとすっきりします。// domain_sample5.jsvar fs = require('fs');var domain = require('domain');var d = domain.create();function countChars(filename, callback) { fs.readFile(filename, 'utf-8', function(err, data) { if (err) throw err; callback(data.length); });}function main(args) { // 関数を呼び出す時にドメインをバインドする。 d.run(function() { countChars(args[0], function(length) { console.log(length); }); });}d.on('error', function(err) { console.log(err.message);});main(process.argv.slice(2));