我目前有一个包含以下内容的数据库连接模块:
var mongodb = require("mongodb");
var client = mongodb.MongoClient;
client.connect('mongodb://host:port/dbname', { auto_reconnect: true },
function(err, db) {
if (err) {
console.log(err);
} else {
// export db as member of exports
module.exports.db = db;
}
}
);
然后,我可以成功访问它,执行以下操作:
users.js
var dbConnection = require("./db.js");
var users = dbConnection.db.collection("users");
users.find({name: 'Aaron'}).toArray(function(err, result) {
// do something
});
但是,如果我改为导出module.exports = db
,即尝试将exports
对象分配给db
对象而不是使其成为导出的成员,并尝试访问它users.js
通过var db = require("./db.js");
对象未定义,为什么?
如果是因为设置连接有延迟(不应require()
等到模块在分配module.exports的值之前完成其代码的运行?),那么为什么这两个都没有例子有用吗?
one.js
setTimeout(function() {
module.exports.x = {test: 'value'};
}, 500);
two.js
var x = require("./one");
console.log(x.test);
OR
one.js
setTimeout(function() {
module.exports.x = {test: 'value'};
}, 500);
two.js
setTimeout(function() {
var x = require("./one");
console.log(x.test);
}, 1000);
在两种情况下运行$ node two.js
都会打印undefined
而不是value
。
答案 0 :(得分:3)
这里有3个要点可以理解,然后我会详细解释它们。
如你所知,这是一个时机。 node.js无法知道module.exports将在以后更改。那不是问题。怎么会知道呢?
当require
运行时,它根据您输入的路径找到满足其要求的文件,读取并执行它,并缓存module.exports,以便其他模块可以require
相同的模块而不必重新初始化它(这将搞乱变量范围等)。
client.connect是一个异步函数调用,因此在执行它之后,模块完成执行,require
调用存储module.exports引用的副本并将其返回给users.js。然后你设置module.exports = db
,但为时已晚。您正在使用对db的引用替换module.exports引用,但节点require
缓存中的模块导出指向旧对象。
最好将module.exports定义为一个获取连接的函数,然后将其传递给回调函数,如下所示:
var mongodb = require("mongodb");
var client = mongodb.MongoClient;
module.exports = function (callback) {
client.connect('mongodb://host:port/dbname', { auto_reconnect: true },
function(err, db) {
if (err) {
console.log(err);
callback(err);
} else {
// export db as member of exports
callback(err, db);
}
}
)
};
警告:虽然这超出了本答案的范围,但请务必小心上述代码,以确保正确关闭/返回连接,否则会泄漏连接。
答案 1 :(得分:1)
不应该要求()等待模块完成其代码的运行 在分配module.exports的值之前?
module.exports.db
正在设置回调,此操作是异步的,因此在user.js
中您无法获得db.collection
。
connect
回调中的add个集合会更好。
您可以使用this答案更改代码,并在其他模块中使用shared connection
。
答案 2 :(得分:1)
是的,dbConnection.db
未定义,因为连接是异步连接的,这意味着根据定义,节点代码只是继续执行而无需等待建立数据库连接。
不应该要求()等到模块完成运行其代码之后再分配module.exports的值吗?
不,它只是不起作用。 require
用于始终存在的代码。数据库连接不是代码,并不总是存在。最好不要混淆这两种类型的资源以及如何从您的程序中引用它们。
答案 3 :(得分:0)
问题是什么?这就是require
的工作原理 - 它同步获取模块并传递出口。
你建议“等到代码运行”可以通过两种方式回答:
setTimeout
已成功完成。学习将针对未来的异步回调与实际线程分开。if (true) { thisruns(); } else { thiswontrunever(); }
怎么办?)