node.js Domain 時代のエラー処理のコーディングパターン
id:kazuhooku さんの記事 node.js におけるエラー処理のコーディングパターン (もしくは非同期 JavaScript における例外処理。
ナイスです! なんと素晴らしいタイミングでのブログでしょうか!
「東京Node学園 5時限目」で id:koichik さんのプレゼンで node-v0.7.8 から isaacs 版 Domain が導入されるという発表がありましたが、予定通り昨日 Domain 機能付きの node-v0.7.8 がリリースされました。
しかもDomain のドキュメント付きです。 http://nodejs.org/docs/v0.7.8/api/domain.html
ちょうど id:kazuhooku さんの記事の例は node.js の新機能 Domain を教科書通りに適応するとどうなるのか紹介するのにぴったりのお題なので
Node.js v0.8 の新機能 Domain の使い方
について書かせていただきます。
Node.jsの新機能 Domain を使う
まずドメインを利用してエラー処理するには
- domain モジュールの require()
- domain オブジェクトの生成
- domain で受けるエラーリスナーの登録
- domain とエラーハンドリングとの結び付け
- eventEmitter の場合は Domain.add
- 関数の場合は Domain.bind
といった段取りが必要です。
id:kazuhooku さんの記事にあるコードを、Domain 使ったらどのようなコーディングパターンが考えられるかいくつ例示してみたいと思います。。(注:動作には node-v0.7.8 以降が必要です。)
今回3つのパターンで考えました。
- パターン1: コールバック関数にドメインを結びつけてエラーハンドリングを行う
- パターン2: 関数呼び出し時にドメインを結びつけてエラーハンドリングを行う
- パターン3: 関数定義時にドメインを結びつけてエラーハンドリングを行う
ではサンプルコードとともにドメインを使ったコーディングパターンを例示していきます。
パターン1: コールバック関数にドメインを結びつける方法
まずはエラーオブジェクトを含むコールバックでドメインとエラーを結びつける場合です。
// domain_sample1.js var 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 hoge ENOENT, open 'hoge' |
はい、きれいなコードになりましたね。
パターン2: 関数呼び出しに時にドメインを結びつける方法
関数を呼び出すときにバインドしたい場合は、
// domain_sample2.js var 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.js var 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() を使う
id:koichik さんからのコメントにあるよう domain.bind(cb, true) だと throw しなくてもエラーをひっかけてくれます。(呼ばれる関数の第一引数が Error インスタンスであることが条件ですけど) これをまとめて domain.intercept() という関数になってます。
ということで domain.intercept() を使うともっとすっきりに書けます。
// domain_sample4.js var 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 hoge ENOENT, open 'hoge' |
ありがとうございます。 > id:koichik さん。
追記の追記: Domain.run() を使う。
またまた id:koichik さんから twitter 経由で Domain.run() の使い方を教えていただきました。(実はマニュアルには記載されていませんけど)→ id:koichik さんからのコメントで public API としてドキュメント化されました。https://github.com/joyent/node/commit/c0a9985da7a7d3f6ebc51805d6955d92b1bd6e78
これを使うと上記パターン2(関数呼び出しに時にドメインを結びつける方法)のコードで (d.bind(fn))(args) と書いていたところが d.run(function() {fn(args)} と書けて、もっとすっきりします。
// domain_sample5.js var 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)); |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
2011-07-25 mass Framework class模块 v5