2012-01-29 15 views
2

我正在使用快遞框架爲node.js中的網站編寫登錄。但是,代碼以奇怪的順序執行,我不知道如何解決它。下面是相關的代碼的簡化版本:使用快遞在node.js中編寫登錄

app.post('/login', function(req, res){ 
    var login_error; 
    if (!req.session.username) { //if no one is logged in 
     if (req.body.user != undefined && req.body.pass != undefined) { 
      client.query('USE data', function(error, results) {} 
     }); 
     client.query('SELECT id FROM user WHERE username=? AND password=?',[reg.body.user, req.body.pass], 
     function(err, results,fields) { 
      if (err || results.length == 0) { 
       login_error=1; 
       console.log('a '+login_error); //LINE A 
      } 
     }); 
    } 
    console.log('b '+login_error); //LINE B 
    if (login_error == undefined) { 
     req.session.username=req.body.user; 
    } 
    client.end(); 
} 
res.render('login', { 
    user: req.session.username, 
    login_error: login_error 
}); 

頁面總是與login_error =未定義的渲染,即使用戶名/密碼組合是不是在數據庫中。在這種情況下,LINE A正在打印login_error = 1,但LINE B正在打印login_error = undefined。此外,LINE B在LINE A之前打印,即使它稍後出現。我不確定這裏發生了什麼事。

+0

直到'function(req,res)'主體完成後,纔會發生回調函數(err,results,fields)'。 – 2012-01-29 21:58:52

回答

6

這是因爲回調工作的方式而發生的。在此代碼中:

client.query('SELECT id FROM user WHERE username=? AND password=?',[reg.body.user, req.body.pass], 
    function(err, results,fields) { 
     if (err || results.length == 0) { 
      login_error=1; 
      console.log('a '+login_error); //LINE A 
     } 
    }); 

包含行A的函數不會立即執行。相反,client.query會立即返回並繼續執行,並朝着B行。

然後,當選擇查詢返回時,您的回調函數將執行。所以,爲了執行,它可能會在 B行之後出現,即使它事先出現在源代碼中。

考慮這個例子

client.query('SELECT 1 AS Res', function(err, results) { 
    console.log(results.fields.Res); 
}); 

client.query('SELECT 2 AS Res', function(err, results) { 
    console.log(results.fields.Res); 
}); 

,你可能會發現這產生了以下的輸出:

2 
1 

由於第二查詢可能返回比第一快。

這是Node的電源 - 代碼不會阻塞,它是異步的,所以它的快速

爲了讓您的示例按預期工作,您應該重構它以在獨立函數中調用需要了解查詢結果的代碼。例如更多的東西是這樣的:

function processLogin(login_error) { 
    console.log('b '+login_error); //LINE B 
    if (login_error !== true) { 
    req.session.username=req.body.user; 
    } 

    res.render('login', { 
     user: req.session.username, 
     login_error: login_error 
    }); 
} 

app.post('/login', function(req, res){ 
    if (!req.session.username) { //if no one is logged in 
     if (req.body.user != undefined && req.body.pass != undefined) { 
      client.query('USE data', function(error, results) {} 
     }); 
     client.query('SELECT id FROM user WHERE username=? AND password=?',[reg.body.user, req.body.pass], function(err, results,fields) { 
      if (err || results.length == 0) { 
       process_login(true); 
      } else { 
       process_login(false); 
      } 
     }); 
    } 
    client.end(); 
} 

此代碼將無法正常工作馬上,但是請注意我是如何移動的res.render呼叫到一個函數,這我是從客戶端的回調調用。查詢。現在,您需要允許回調訪問res變量,或者通過將其設置爲全局變量(如果您在專用的「登錄」模塊內,這很好),或者將它傳遞給作爲參數的功能,這可能是優選的。

僅僅因爲一行代碼出現在另一行之後,並不一定意味着它會在它之後執行,如果涉及到回調。你可能熟悉的一些類似的行爲是超時;考慮這個問題:

setTimeout(function() { 
    console.log(1); 
}, 1000); 
console.log(2); 

在這種情況下,它應該是顯而易見的,你會看到以下內容:

2 
1 

這正是與回調的東西,如MySQL查詢相同。而不是等待client.query返回的整個進程,執行繼續進行,並將依賴於client.query結果的所有內容放入發送給client.query的回調中。

+1

感謝您的徹底和非常有幫助的回覆!我不得不改變一些東西;我認爲你在某些地方編寫了processLogin,而在其他地方編寫了processLogin,並且我必須將res和req傳遞給process_login,以便他們可以訪問所有內容。 – ario 2012-01-29 22:31:50

+0

@ario是的,你是對的,我搞砸了功能名稱,oopsie!很高興你發現它有幫助。節點首先是一個扭曲的頭,並且寫意大利麪代碼非常容易。你讀過http://nodebeginner.org了嗎?這是一本很棒的書。 – Howard 2012-01-29 22:35:25

+0

非常好,寫得很好的答案。 – user482594 2012-01-29 23:38:04