2010-12-05 17 views
18

我是一名經驗豐富的軟件開發人員,但對JS和節點來說都很新穎。我並不是超級嵌套代碼的忠實粉絲,所以我一直在試圖將回調分解爲各自的功能。儘管在回調觸發時如何確定範圍,我仍然遇到了麻煩。挖掘周圍我讀到,如果我在回調上創建了一個閉包,它將起作用,但它看起來並不像我預期的那樣工作。如何維護node.js回調中的作用域?

下面是不是爲我工作的那種代碼的一個非常簡單的版本:

function writeBody() 
{ 
    res.end("<h1> Hooray! </h1>"); 
} 

http.createServer(function(req, res) 
{ 
    res.writeHead('Content-Type', 'text/html'); 
    setTimeout(function(){writeBody()}, 2000); 
}).listen(8000); 

我認爲在()的函數封閉包裹writeBody()調用我會範圍我需要超時後,但是當writeBody()火災,我得到

ReferenceError: res is not defined

誰能告訴我什麼愚蠢的事情我做錯了嗎?

回答

35

基本上,不是閉包如何工作,函數繼承它們的外部作用域。

// this function only inherits the global scope 
function writeBody() 
{ 
    res.end("<h1> Hooray! </h1>"); 
} 

http.createServer(function(req, res) // a new local varaible res is created here for each callback 
{ 
    res.writeHead('Content-Type', 'text/html'); 
    // annonymous function inheris both the global scope 
    // as well as the scope of the server callback 
    setTimeout(function(){ 

     // the local variable res is available here too 
     writeBody() 

    }, 2000); 
}).listen(8000); 

爲了讓剛剛工作通過res對象到函數,因爲它在超時回調的可用。

function writeBody(res) 
{ 
    // NOT the same variable res, but it holds the same value 
    res.end("<h1> Hooray! </h1>"); 
} 

http.createServer(function(req, res) 
{ 
    res.writeHead('Content-Type', 'text/html'); 
    setTimeout(function(){ 
     writeBody(res); // just pass res 
    }, 2000); 
}).listen(8000); 

但是,你需要注意的事情是這樣的:

for(var i = 0; i < 10; i++) { // only one i gets created here!() 
    setTimeout(function() { 
     console.log(i); // this always references the same variable i 
    }, 1000); 
} 

這將打印10十倍,因爲基準是相同的,i得到增加一路攀升至10。如果你想要得到不同的數字,你需要爲每個變量創建一個新的變量,通過將setTimeout包裝成一個匿名的自我函數,你可以通過i作爲參數傳遞,或者調用其他一些方法來設置timouet並接收i作爲參數。

// anoynmous function version 
for(var i = 0; i < 10; i++) { 
    (function(e){ // creates a new variable e for each call 
     setTimeout(function() { 
      console.log(e); 
     }, 1000); 
    })(i); // pass in the value of i 
} 

// function call version 
for(var i = 0; i < 10; i++) { 
    createTimeoutFunction(i); 
} 
+0

哦!咄! * facepalm *這有助於減輕痛苦,我只是在腦海中產生了一個轉變時刻。 – MahatmaManic 2010-12-05 08:21:27

2

您也可以使功能嵌套的,所以它們共享範圍,即

http.createServer(function(req, res) 
{ 

    function writeBody() 
    { 
     res.end("<h1> Hooray! </h1>"); 
    } 

    res.writeHead('Content-Type', 'text/html'); 
    setTimeout(function(){writeBody()}, 2000); 
}).listen(8000); 

我常常覺得這是不是總是通過一組變量中,以保持在範圍更容易,儘管這意味着您無法在其他地方重複使用該功能。

+1

我很感謝你花時間回答,而且你可能是對的,它通常更快,更容易,但作爲風格的一般規則,我儘量避免在可能的地方築巢。在這個簡單的例子當然不是很糟糕,但當事情變得更加複雜時(比如查找,閱讀和處理文件),它會變得很難看。 – MahatmaManic 2010-12-09 15:04:00

0

您可以通過響應在回調如此:

http.createServer(function(req, res) 
{ 
    res.writeHead('Content-Type', 'text/html'); 
    setTimeout(function(){writeBody(res)}, 2000); 
}).listen(8000); 
0

其實我喜歡賈斯汀·科馬克的答案。這是我最近編碼的一個更極端的例子。

var Func4 = function(req, res) 
{ 
    var collectionName = "parts"; 
    var f0 = function() {mongodbClient.collection(collectionName, f1);}; 
    var f1 = function(err, coll) {coll.ensureIndex("item", f2);}; 
    var f2 = function(err, indexname) 
    { 
    res.writeHead(200, {'Content-Type': 'text/plain'}); 
    res.write("index name = " + indexname); 
    res.end(); 
    }; 
    f0(); 
}; 

大多數人會告訴我(他們這樣做),這是編寫該代碼的正確方法。

var Func4 = function(req, res) 
{ 
    var collectionName = "parts"; 
    mongodbClient.collection(collectionName, function(err, coll) { 
    coll.ensureIndex("item", function(err, indexname) { 
     res.writeHead(200, {'Content-Type': 'text/plain'}); 
     res.write("index name = " + indexname); 
     res.end(); 
    })}); 
}; 

也許我是一個n00b,但我發現嵌套的回調有點難以遵循。我也承認一堆f0,f1,f2的功能是跛腳的。無論哪種方式,這是範圍的一個很好的例子。