Node 初探異步編程

從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時註冊的函數。

相關文章
相關標籤/搜索