zoukankan      html  css  js  c++  java
  • 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));
    
  • 相关阅读:
    poj(1458)(最长公共子序列)
    二叉搜索树
    hdu1087
    poj3641(学习了)
    平年和闰年的由来。。。。
    Linux system函数返回值(转)
    VS2010单元测试(转)
    QT QTableWidget 用法总结(转)
    QT显示图片(转)
    Qt正则表达式类QRegExp(转)
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2609025.html
Copyright © 2011-2022 走看看