require在node.js中的行为

我目前有一个数据库连接模块包含以下内容:

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对象,而不是使其成为exports的成员,并尝试通过var db = require("./db.js");users.js访问它var db = require("./db.js"); 对象是不确定的,为什么?

如果是因为连接build立有延迟(不require()等到模块在分配module.exports的值之前运行它的代码),那么为什么这两个例子都不起作用呢?

one.js

 setTimeout(function() { module.exports.x = {test: 'value'}; }, 500); 

two.js

 var x = require("./one"); console.log(x.test); 

要么

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

这里有三个关键要点,然后我会详细解释。

  1. module.exports是一个对象,对象通过JavaScript中的引用副本传递。
  2. require是一个同步函数。
  3. client.connect是一个asynchronous函数。

如你所说,这是一个时机的事情。 node.js不能知道module.exports将在以后更改。 这不是问题。 它怎么会知道?

require运行时,它会根据您input的pathfind满足其要求的文件,读取并执行它,并cachingmodule.exports,以便其他模块可以require相同的模块,而不必重新初始化它弄乱variables范围等)

client.connect是一个asynchronous的函数调用,所以执行后,模块完成执行, require调用存储module.exports引用的一个副本,并将其返回给users.js。 然后你设置module.exports = db ,但是为时已晚。 您正在使用对db的引用replacemodule.exports引用,但节点中的模块导出requirecaching指向旧对象。

最好将module.exports定义为一个函数,它将获得一个连接,然后将其传递给一个callback函数,如下所示:

 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); } } ) }; 

警告:虽然这不在本答案的范围之内,但要非常小心以上代码,以确保正确closures/返回连接,否则将泄漏连接。

是的, dbConnection.db是未定义的,因为连接是asynchronous进行的,这意味着节点代码只是继续执行而不需要等待build立数据库连接。

在分配module.exports的值之前,不应该要求()等到模块完成运行其代码?

不,它只是不这样工作。 require是代码总是在那里。 数据库连接不是代码,并不总是在那里。 最好不要混淆这两种types的资源,以及如何从您的程序中引用它们。

在分配module.exports的值之前,不应该'require()等到模块完成运行它的代码之前?

module.exports.db在callback中设置,这个操作是asynchronous的,所以在user.js你不能得到db.collection 。 在connectcallback中添加集合会更好。

您可以使用此答案更改您的代码并在其他模块中使用shared connection

问题是什么? 这就是require工作的方式 – 它将模块同步到您的输出。

您build议“等到代码运行”可以回答两个方面:

  1. 它等待代码运行。 setTimeout已成功完成。 学习从实际线程中分离未来的asynchronouscallback。
  2. 如果你的意思是“直到所有的asynchronouscallback运行”,这是胡说八道 – 如果其中一些根本不运行,因为它等待,我不知道,鼠标点击,但用户没有附加鼠标呢? (甚至你怎么定义'所有的代码已经运行?'每个语句至less运行一次?怎么样if (true) { thisruns(); } else { thiswontrunever(); } ?)