node.js中require的行为

时间:2014-01-12 17:55:40

标签: javascript node.js mongodb express

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

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

4 个答案:

答案 0 :(得分:3)

这里有3个要点可以理解,然后我会详细解释它们。

  1. module.exports是一个对象,对象通过JavaScript中的引用副本传递。
  2. require是同步功能。
  3. client.connect是异步功能。
  4. 如你所知,这是一个时机。 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.collectionconnect回调中的add个集合会更好。

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

答案 2 :(得分:1)

是的,dbConnection.db未定义,因为连接是异步连接的,这意味着根据定义,节点代码只是继续执行而无需等待建立数据库连接。

  

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

不,它只是不起作用。 require用于始终存在的代码。数据库连接不是代码,并不总是存在。最好不要混淆这两种类型的资源以及如何从您的程序中引用它们。

答案 3 :(得分:0)

问题是什么?这就是require的工作原理 - 它同步获取模块并传递出口。

你建议“等到代码运行”可以通过两种方式回答:

  1. 等待代码运行。 setTimeout已成功完成。学习将针对未来的异步回调与实际线程分开。
  2. 如果你的意思是“直到所有的异步回调都运行”,这是无稽之谈 - 如果它们中的一些根本没有运行会怎么样,因为它等待,我不知道,鼠标点击,但是用户没有鼠标贴? (以及你如何定义'所有代码都已运行?'每个语句至少运行一次?那么if (true) { thisruns(); } else { thiswontrunever(); }怎么办?)