今天要介紹的是node的事件迴圈機制,他是一些互動系統的基本概念,他更是javascript語言核心的一部分。因為對於使用瀏覽器的使用者,javascript就是通過處理使用者的click,mouse等相關的事件來處理使用者的請求的。對於像node這樣的伺服器來說,事件迴圈也同樣起到相當重要的作用,比如用來處理來自用戶端的request請求。下面我們就對node的事件迴圈做個簡單的介紹。
node採用單線程的處理機制,對所有的I/O請求採用非堵塞的工作方式。也就是說,對於http的請求,資料查詢,檔案的I/O等其他動作,系統不會堵塞當前線程而去等待他直到他返回結果;相反的,他們會立即執行當前請求,發送一個處理當前請求結果的回調事件,然後接著處理新的請求。當之前的回呼函數返回結果的時候,輸出結果。這也就意味著,在node中存在著大量回呼函數用於處理不同請求。回呼函數又經常以級聯的方式初始化其他的回呼函數,這與瀏覽器的處理方式是相當不同的。node缺乏並發性,他只會使用伺服器上的一個cpu,而不是多個。使用多個cpu的問題在於,需要不斷的協調多個cpu之間的運行,這就帶來了不同cpu之間的通訊,交換彼此的狀態資訊,這是一種比較複雜的處理模式。而node使用單線程的方式迴避了這一問題。node的這種處理機制更為高效,下面我們舉個例子來說明。
假設我們要去快餐店吃飯,有兩種不同服務模式的快餐店,一種是基於事件驅動的(我們的node伺服器),一種不是(像iis,apache)。對於傳統的伺服器,在接收到你的請求之後,直到他完成你的請求,否則他不會去接待下一個使用者。當服務員輸入你的訂單之後還有很多事情要做,處理你的支付,幫你倒水,還有一段時間(不確定時間長度)去等待廚房準備好你的漢堡。服務員(相當於伺服器上的線程)每次只能接待一位顧客,直到完成當前顧客的接待之後,才會去接待下一位顧客。很顯然,這種方式效率不高,他浪費了太多的時間在等待廚房做漢堡的工作上。而現實中的快餐店採用的是另外一種模式,當接收到你的訂單之後,他會給你一個號碼牌,這個號碼牌就相當於回呼函數。接著他會去接待下一位顧客。當你的訂餐準備好之後,服務員會呼叫你的號碼叫你來取餐。這就是node採用的模式,看得出他要高效的多。
node不太適用單個請求處理時間比較長的情境;適用於並發比較多,每個請求所需處理時間比較短的情境。我們來看一個例子:
EE = require('events').EventEmitter;ee = new EE();die = false;
ee.on('die', function() { die = true;});setTimeout(function() { ee.emit('die');}, 100);
while(!die) {}
console.log('done');
你可以猜猜‘done’會不會輸出。答案是明顯的,node根本沒有機會回調timeout方法,程式會堵塞在while迴圈處。你也可以執行一個瀏覽器的版本:
var die = false;setTimeout(function() { die=true}, 100);while(!die) {}alert('done');
瀏覽器也同樣不會彈出‘done’。這個簡單的例子可以很好的解釋javascript的單線程執行,javascript只有把當前手頭上的事情處理完畢之後,才會去做別的事情。就像上面的例子,javascript會一直專心處理while處的代碼,根本沒有機會去執行timeout的方法。這也解釋了為什麼node不適用於處理單個請求時間比較長的情境(例如,處理回呼函數返回內容需要很長的時間)。因為在處理完當前請求之前,node沒有機會去接待下一個請求,處理別的請求的回調,這會直接影響其他使用者的體驗。
開發node程式,有兩條建議:
1.一旦代碼的初始化工作完成後,就啟用事件;
2.如果遇到比較耗時的操作,可以考慮把他委託給web worker去執行。
參考資料:
http://www.infoq.com/cn/articles/what-is-nodejs
http://www.nodebeginner.org/index-zh-cn.html
http://baike.baidu.com/view/3974030.htm?subLemmaId=3974030&fromenter=nodejs