22

モジュールを非同期で初期化し、いくつかのアイデアを思いつきたいと思います。Mongo およびその他のデータからのコレクションのリストを含む DB オブジェクトが必要です./が、簡潔にするためにファイルのリストで十分です。

require('db')毎回同じオブジェクトを返す必要があるため、関数またはクラスをエクスポートできません。


最初に頭に浮かんだ最も簡単なことは、後で割り当てmodule.exportsObject入力することです。

var exports = {};
module.exports = exports;

require('fs').readdir('.', function(err, files) {
  exports.error = err;
  exports.files = files;
});

悪い点 — リストの準備が整ったときに、外部からは実際にはわかりません。また、エラーをチェックする良い方法もありません。


私が思いついた2 番目EventEmitterの方法は、継承して、DB の準備ができているかエラーが発生したことを全員に通知することです。すべてがOKなら、続けてください。

var events = require('events');
var util = require('util');

function Db() {
  events.EventEmitter.call(this);
  this.ready = false;
  this.files = null;
  this.initialize();
}

util.inherits(Db, events.EventEmitter);

Db.prototype.initialize = function() {
  if (this.ready)
    return this.emit('ready');

  var self = this;
  require('fs').readdir('.', function(err, files) {
    if (err)
      return self.emit('error', err);

    self.files = files;
    self.ready = true;
    self.emit('ready');
  });
};

module.exports = new Db();

そして今、私はそれがより合理的だと思います:

// db.js
var exports = {init: init};
module.exports = exports;

function init(callback) {
  callback = (typeof callback === 'function') ? callback : function() {};
  require('fs').readdir('.', function(err, files) {
    delete exports.init;
    exports.result = files; // that's pretty much what I need,
                            // so don't mind result slightly differs
                            // from previous cases
    callback(err);
  });
}
// main.js
var db = require('./db');

// check for `db.init` presence maybe...

db.init(function(err) {
  return err ? console.error('Bad!')
             : console.log(db); // It works!
});

何を選ぶべきですか、なぜですか? そのようなアイデアは一般的に、特に私のオプションはどれほど悪いですか?

フィードバックありがとうございます。

4

3 に答える 3

26

TL;DR:起動時にローカル ファイルを読み取るだけreaddirSync()の場合は、代わりに使用してください。readdir()実際にリモート データベースからデータを読み取るか、実行時に I/O を実行する予定がある場合は、オプション #2 - コールバックを使用します。以下に説明とコード例を示します。

詳細な説明:

最初は、これはモジュール/依存関係/require 関連の質問のように思えるかもしれませんが、実際にはそうではありません。これは、非同期コードの処理方法に関する一般的な質問です。説明させてください:

require()基本的に、I/O を処理するノード全体で広く使用されている唯一の同期関数です (ファイルシステムの他のモジュールが必要です)。同期とは、コールバックを呼び出す代わりに、実際にデータを戻り値として返すことを意味します。

非同期プログラミングの最も基本的な 101 ルールは次のとおりです。

非同期のコードを取得して、そのための同期 API を作成することはできません。

requireは、呼び出された の特別な同期バージョンを使用します。モジュールは実際にはプログラムの開始時にのみロードされるため、モジュールの読み取り中に node.js の実行がブロックされるという事実は問題ではありません。readFilereadFileSync

ただし、あなたの例では、必要な段階で行われる追加の非同期 I/Oを実行しようとしています。readdir()したがって、このコマンドの同期バージョンを使用するか、API を変更する必要があります...

だからあなたの問題の背景があります。

次の 2 つの基本的なオプションを特定しました。

  1. 約束を使用する(これは本質的にあなたのEventEmitter例と同じです)
  2. コールバックを使用して(2番目の例はこれをよく示しています)、3番目は次のとおりです。
  3. と呼ばれるコマンドの同期バージョンを使用するreaddir()readdirSync()

簡単にするためにオプション#3を使用しますが、例が示すように、起動時にいくつかのファイルを読み取るだけを計画している場合に限ります。後でDBモジュールが実際にデータベースに接続する場合、または実行時にこれを行う予定がある場合は、今すぐボートに飛び乗って非同期APIを使用してください.

これを覚えている人はもう多くはありませんが、promise は実際には node.js で非同期を処理する方法の元のデフォルトでした。ただし、ノード 0.1.30 では、プロミスが削除され、署名付きの標準化されたコールバックに置き換えられました。function(err, result)これは主に単純化のために行われました。

最近では、非同期呼び出しの大部分が、この標準コールバックを最後のパラメーターとして使用しています。データベース ドライバーがそれを行い、Web フレームワークがそれを行います - それはどこにでもあります。流行のデザインにとどまり、それも使用する必要があります。

プロミスまたはイベントを優先する唯一の理由は、複数の異なる結果が発生する可能性がある場合です。たとえば、ソケットを開いたり、データを受信したり、閉じたり、フラッシュしたりできます。

これはあなたの場合ではありません。モジュールは常に同じことを行います (いくつかのファイルを読み取ります)。したがって、オプション#2です(同期を維持できる場合を除く)。

最後に、わずかに書き直した 2 つの勝利オプションを次に示します。

同期オプション:
起動時のローカル ファイルシステムにのみ適しています

// db.js
var fs = require('fs');
exports = fs.readdirSync('.');

// main.js
var db = require('./db');
// insert rest of your main.js code here

非同期オプション:
DBなどを利用したい場合に。

// db.js
var fs = require('fs'), cached_files;

exports.init = function(callback) {
  if (cached_files) {
    callback(null, cached_files);
  } else {
    fs.readdir('.', function(err, files) {
      if (!err) {
        cached_files = files;
      }
      callback(err, files);
    });
  }
};

// main.js
require('./db').init(function(err, files) {
  // insert rest of your main.js code here
});
于 2012-08-25T21:15:50.970 に答える
5

一般に、モジュールに状態を持たせることは非常に悪い考えです。モジュールは、データではなく関数を公開する必要があります (はい、これにはコード構造を少し変更する必要があります)。データへの参照をモジュール関数にパラメーターとして渡すだけです。

編集:これが最後の例からのアプローチであることに気づきました。私の投票)

モジュール 1:

module.exports = function(params, callback) { ... }

モジュール 2:

var createSomething = require('module1');
module.exports = function(params, callback) { 
   ...
   var db = createSomething(params, function(err, res) {
       ...
       callback(err, res);
   }
}

メインコード:

var createSomethingOther = require('module2');
createSomethingOther(err, result) {
    // do stuff
}
于 2012-08-06T23:02:33.593 に答える
1

私の側では、そのようなモジュールはコールバックを受け取る関数です (また、promise で内部的に構成されている場合は promise も返します ( https://github.com/medikoo/deferredを参照));

コールバックの唯一の問題は、慣例により常に nextTick で呼び出される必要があることです。そのため、すべてのデータが収集されたときにモジュール関数を呼び出す場合でも、次のティックでコールバックを結果セットとともに呼び出す必要があります。

于 2012-08-07T08:41:32.230 に答える