2014-01-12 47 views
1

我現在有一個包含數據庫連接模塊如下:行爲需要的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對象,而不是使其成爲出口成員,並嘗試通過var db = require("./db.js");來訪問users.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

回答

3

有3個關鍵點,瞭解這裏,然後我會詳細解釋它們。

  1. module.exports是一個對象,對象通過JavaScript中的引用副本傳遞。
  2. 要求是同步函數。
  3. client.connect是一個異步函數。

如您所說,這是一個計時的事情。 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); 
      } 
      } 
    ) 
}; 

警告:雖然這是這個答案的範圍之外,很注意上面的代碼以確保關閉/返回適當的連接,否則會泄漏連接。

+0

爲什麼設置'module.exports.db = db'工作?無論我將其設置爲「module.exports」還是「module.exports.db」,都不會發生同樣的事情嗎? – alh

+1

這是不同的,因爲module.exports是一個對象的引用,你不會改變它來引用不同的對象。相反,你正在改變現有的對象。 – Brandon

1

在分配module.exports的值之前,不應'require()等待模塊完成運行其代碼 ?

module.exports.db在回調設置,此操作異步,所以在user.js你不能得到db.collectionconnect回調中的add集合會更好。

您可以使用this答案更改您的代碼並在其他模塊中使用shared connection

+0

如果我將它設置爲'module.exports.db',我可以在'user.js'中獲得它,如果我嘗試將它設置爲'module.exports',這只是一個問題。 – alh

1

是的,dbConnection.db是未定義的,因爲連接是異步進行的,這意味着節點代碼只是繼續執行而不需要等待建立數據庫連接。

不應該要求()等待模塊在分配module.exports的值之前完成運行其代碼?

沒有,它只是不這樣工作。require適用於始終存在的代碼。數據庫連接不是代碼,並不總是存在。最好不要混淆這兩種類型的資源,以及如何從你的程序中引用它們。

+0

我很想知道設置'module.exports'和'module.exports.db'有什麼區別,因爲後者對我來說工作得很好,儘管延遲了。我明白前者不工作的原因,但不明白爲什麼'module.exports.db'不會受到同樣的命運。 – alh

0

這是什麼問題?這就是require的工作原理 - 它同步獲取模塊並將您的輸出傳遞給您。到「等到代碼運行」

您建議可以回答兩個方面:

  1. 它等待,直到代碼運行。 setTimeout已成功完成。學習從實際線程中分離未來的異步回調。
  2. 如果你的意思是「直到所有的異步回調都運行」,這就是無稽之談 - 如果它們中的一些根本沒有運行,因爲它等待,我不知道,點擊鼠標,但用戶沒有鼠標連接? (你怎麼連定義「的所有代碼運行?」每一個聲明至少運行一次?什麼?)