從C/C++轉過來最開始不適應的就是這個了吧。=-=html
Node是單線程,那麼怎麼提升效率?怎麼解決一些阻塞問題?Node的基因裏使用了異步IO,上次在http://www.cnblogs.com/zhangmingzhao/p/7564738.html 已經說到這個問題,Node的異步機制每每伴隨着回調。java
先看一個關於CPU的例子來比較同步和異步:node
同步:CPU須要計算10個數據,每計算一個結果後,將其寫入磁盤,等待寫入成功後,再計算下一個數據,直到完成。web
異步:CPU須要計算10個數據,每計算一個結果後,將其寫入磁盤,不等待寫入成功與否的結果,馬上返回繼續計算下一個數據,計算過程當中能夠收到以前寫入是否成功的通知,直到完成。編程
如過沒有異步,CPU寫入磁盤等待返回的結果的等待時間也被無情的消耗了。來看一段代碼:服務器
咱們期待的結果是1,可是結果確實0。爲何呢?異步
這個就是node的異步機制。咱們先執行了plus,沒有等2秒,而是直接運行下一句打印函數,此時的c值仍是0.編程語言
那怎麼解決這個問題呢?函數
傳入回調函數來解決。測試
咱們來給plus傳入一個callback函數,以下:
運行結果,打印出1。
因爲異步的高效性,node.js設計之初就考慮作爲一個高效的web服務器,做者理所固然地使用了異步機制,並貫穿於整個node.js的編程模型中,新手在使用node.js編程時,每每會羈絆於因爲其餘編程語言的習慣,好比C/C++,以爲無所適從。咱們能夠從如下一段簡單的睡眠程序代碼窺視出他們的區別。
咱們來寫一個Java的:
import java.text.SimpleDateFormat; import java.util.Date; public class Main { public static void main(String[] args) { int i; SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//設置日期格式 for(i = 0; i < 10; i++) { System.out.println(df.format(new Date())); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
運用Java中線程的sleep來獲得以下結果:
2017-09-22 18:53:41
2017-09-22 18:53:43
2017-09-22 18:53:45
2017-09-22 18:53:47
2017-09-22 18:53:49
2017-09-22 18:53:51
2017-09-22 18:53:53
2017-09-22 18:53:55
2017-09-22 18:53:57
2017-09-22 18:53:59
咱們把代碼直譯成Node版本:
function test() { for (var i = 0; i < 10; i++) { console.log(new Date); setTimeout(function(){}, 2000); //睡眠2秒,而後再進行一下次for循環打印 } }; test();
咱們發現這個結果幾乎是同時打印出來,2秒並無起到什麼做用。
setTimeout的第二個參數表示該時間以後在執行第一個參數表示的callback函數。
所以咱們能夠分析, 因爲Node.js的異步機制,setTimeout每一個for循環到此以後,都註冊了一個2秒後執行的回調函數而後當即返回立刻執行console.log(new Date),致使了全部打印的時間都是同一個點。然而這個函數僅僅是註冊,空函數什麼事都沒作。那麼我這樣呢:
for (var i = 0; i < 10; i++) { setTimeout(function() { console.log(new Date); }, 2000); }
結果是等了2秒後,一會兒打印了10個
仍是異步特性,咱們能夠這樣理解,for循環裏每次setTimeout註冊了2秒以後執行的一個打印時間的回調函數,沒有等2秒,而是當即返回,再執行setTimeout,如此反覆直到for循環結束,由於執行速度太快,致使同一個時間點註冊了10個2秒後執行的回調函數,而後for循環結束,所以致使了2秒後全部回調函數的當即執行。
繼續測試,咱們加上 console.log("before FOR: " + new Date)和以後console.log("after FOR: " + new Date)
console.log("before FOR: " + new Date) for (var i = 0; i < 10; i++) { setTimeout(function() { console.log(new Date); }, 2000); } console.log("after FOR: " + new Date)
這個與一開始的例子差很少,中間的延遲不影響下面代碼的運行。
before和after幾乎在同一時刻運行
而定時器中的回調函數則按要求的2秒以後執行,也是同一秒內執行完畢。那麼如何實現最初Java語言每隔2秒打印一個系統時間的需求函數呢,這樣寫,結果也能夠達到要求:
function test() { for (var i = 0; i < 10; i++) { console.log(new Date); wsleep(2000); //睡眠2秒,而後再進行一下次for循環打印 } }; test(); function wsleep(milliSecond) { var startTime = new Date().getTime(); while(new Date().getTime() <= milliSecond + startTime) { } }
可是在實際狀況中是不會這麼寫的,這樣阻塞了CPU20s的時間,而不能幹其餘事,異步的存在就是提升效率。
還有這樣寫可能會更幫助理解:
for (var i = 0; i < 10; i++) { setTimeout(function () { console.log(new Date()); }, 2000*(i+1)); }
咱們發現打印的都是結果i爲0時註冊的函數。