如何导出只能在asynchronouscallback中使用的对象?

我有一个db.js文件,我在其中build立了一个MongoDB连接。 我想导出数据库对象到我的主app.js文件中:

// db.js require('mongodb').MongoClient.connect(/* the URL */, function (err, db) { module.exports = db; }); // app.js var db = require('./db'); app.get('/', function (req, res) { db.collection(/* … */); // throws error }); 

错误是:

TypeError:对象#没有方法“集合”

那么,如何正确导出db对象呢?

正如elclanrs 的意见所build议的,最好的select是出口一个承诺:

 // database.js var MongoClient = require('mongodb').MongoClient, Q = require('q'), connect = Q.nbind(MongoClient.connect, MongoClient); var promise = connect(/* url */); module.exports = { connect: function () { return promise; } } // app.js var database = require('./database'); database.connect() .then(function (db) { app.get('/', function (req, res) { db.collection(/* … */); }); }) .catch(function (err) { console.log('Error connecting to DB:', err); }) .done(); 

(我在这里使用真棒Q库。)


下面是我的答案的旧版本,为了历史的缘故(如果你不想使用承诺,而不是走这条路,你应该使用马特的答案 )。

它的缺点是,它会打开一个连接,每次你require('database.js) (令人失望!)

 // DO NOT USE: left for the sake of history // database.js var MongoClient = require('mongodb').MongoClient; function connect(cb) { MongoClient.connect(/* the URL */, cb); } module.exports = { connect: connect } // app.js var database = require('./database'); database.connect(function (err, db) { app.get('/', function (req, res) { db.collection(/* … */); }); }); 

你不能这样做,因为引用文档 :

请注意,分配给module.exports必须立即完成。 它不能在任何callback中完成。

相反,您可以在callback中指定module.exports属性 ,因此这将起作用。

 // db.js require('mongodb').MongoClient.connect(/* the URL */, function (err, db) { module.exports.instance = db; }); // app.js var db = require('./db'); // some time later (when `.instance` is available) app.get('/', function (req, res) { db.instance.collection(/* … */); }); 

然而, 一段时间后有一点痛苦,所以你可能只想使用某种callback;

 // db.js var queue = []; var instance = null; require('mongodb').MongoClient.connect(/* the URL */, function (err, db) { instance = db; while (queue.length) { queue.pop()(instance); } }); module.exports.done = function (callback) { if (instance === null) { queue.push(callback); } else { callback(instance); } }; // app.js require('./db').done(function (db) { app.get('/', function (req, res) { db.collection(/* … */); }); }); 

上面还处理了连接done() 通过done()附加处理程序的情况。

服务器通常有3个阶段:init,serve和uninit。 这似乎是显而易见的,但是当你开始从头开始编写服务器(即在Java中,你开始从HttpServletinheritance),有时你忘记了如何做的事情…

在启动阶段,您必须打开数据库连接(池)并将对象保存在某处(通常在您的db.js模块中)。 然后在服务阶段从db.js检索mongodb连接。

相关: 如何从节点mongo本机驱动程序获得db的实例?

在你的代码中:

 // db.js require('mongodb').MongoClient.connect(/* the URL */, function (err, db) { module.exports = db; }); // app.js var db = require('./db'); app.get('/', function (req, res) { db.collection(/* … */); // throws error }); 

你已经在db.js类中调用了connect ,但它是asynchronous的。

app.js的调用require在行为上是同步的,所以它总是会收到一个未定义的值(因为在db.js执行完成时exports不会被赋值)。

我build议保持简单。

我通常使用的选项是应用程序代码进行连接,并且在完成之前不会开始监听HTTP连接。 然后,我将通过调用一个命名方法来初始化每个route文件,并将数据库连接传递给它。

或者,您可以在每个模块中始终调用连接,但是caching该值。 (连接调用将需要在路由callback代码中调用,以便路由被立即定义,而不是在连接实际build立时定义)。

 // db.js var _db = null; exports = function(callback) { if (!_db) { _db = {}; // only one connection, so we'll stop others from trying require('mongodb').MongoClient.connect(/* the URL */, function (err, db) { _db = db; callback(err, db); }); } else { callback(null, _db); } }; // app.js var db = require('./db'); db(function(err, connection) { // store the connection value here and pass around, or ... // call this always in each file ... }); /// or ... app.get('/', function (req, res) { db(function(err, connection) { connection.collection(/* … */); }); }); 

或者,您可以使用MongooseJS(本地NodeJS MongoDB驱动程序的包装器),如果连接不可用,命令等将排队。