2016-05-21 48 views
1

我看到這篇文章:https://www.codementor.io/nodejs/tutorial/manage-async-nodejs-callback-example-code,運行代碼後,我確認nodejs是異步的。 但是我創建了2個js文件來再次測試nodejs的異步功能。Nodejs實際上是異步嗎?

文件1:callback_example.js

exports.countless = function(callback){ 
    var date = new Date(); 
    console.log("*" + date.getSeconds()); 
    var x = 0; 
    for(var i = 1; i <= 1000000000; i++){ 
     x++; 
    } 
    callback(x); 
    date = new Date(); 
    console.log("**" + date.getSeconds()); 
} 

exports.check = function(callback){ 
    var date = new Date(); 
    console.log(date.getSeconds()); 
    callback(123); 
    date = new Date(); 
    console.log(date.getSeconds()); 
} 

文件2:call.js

var call = require('./callback_example'); 

call.countless(function(x){ 
    console.log(x); 
}); 

call.check(function(x){ 
    console.log(x); 
}); 

當我在終端執行call.js爲node call,我看到無數次()完成後,再檢查()運行。這意味着nodejs是synchronus?爲什麼?任何人都可以幫我解答嗎?非常感謝你!

+0

繪製過於寬泛的聲明*「nodejs是異步的」*但它當然可以使用異步操作 – charlietfl

回答

2

node.js使用V8的Javascript引擎,它執行一行接一個的Javascript腳本。如果你在你的問題中編寫了諸如你的countlesscheck方法的順序編碼語句,那麼它們就像幾乎任何其他編程語言一樣同步執行。

這是來自https://nodejs.org/en/的node.js描述的一部分。

Node.js使用事件驅動的非阻塞I/O模型,使其輕量且高效。

我認爲,這比描述node.js是異步的,因爲它更好地描述了它的實際功能。

只有使用某些外部接口(如網絡)的真正的異步操作在node.js中實際上是非阻塞的。在這種情況下,調用非阻塞函數開始操作,然後在Javascript的下一行繼續執行Javascript。當非阻塞操作在將來完成一段時間時,事件被插入到事件隊列中,並且當V8引擎完成執行當前的執行線程時,該事件可以從事件隊列中拉出並且回調將被調用。

您無法在純Javascript中從頭開始編寫真正的異步操作(實際代碼在後臺執行)。您需要外部接口(如網絡,文件I/O等)的幫助才能創建實際的異步操作。你可以用定時器來模擬一個,但實際上並不是異步的,因爲在後臺沒有任何東西實際執行。定時器只是改變事物運行的時間(它們實際上並不與Javascript執行並行)。

下面是一個異步操作中的node.js一個例子:

var fs = require('fs'); 

console.log("one"); 
fs.readFile('temp.txt', function(err, data) { 
    if (err) { 
     console.log(err); 
    } else { 
     console.log("got data"); 
    } 
}); 
console.log("two"); 

這將產生以下輸出:

one 
two 
got data 

fs.readFile()操作實際上是異步的。在你調用它之後,它會在後臺完成它的工作,而後面的語句中的其餘部分繼續執行。當它完成時,在將來的某個時候,它會調用它的回調函數,包含錯誤或數據。

+1

我認爲你關於事物不是「真正」異步的註釋很混亂。對於所有的意圖和目的,promises和'setTimeout'確實是異步的,因爲它們在「將來的某個時刻」完成。 –

+1

@DanPantry - 但是,它們不會在後臺執行任何操作,所以'setTimeout()'所做的就是移動代碼的時間。我在答覆中提到了這一點,試圖說明重要和相關的差異。 'fs.readFile()'在後臺異步執行真正的工作。 'setTimeout(fn,1000)'在後臺沒有做任何真正的工作 - 它只是改變了函數被調用時的時間。 – jfriend00

+0

@toilanvd - 這是否回答您的問題?如果是這樣,請點擊答案左邊的綠色複選標記以向社區表明這一點,併爲您在堆棧溢出時遵循適當的程序獲得一些信譽點。 – jfriend00

1

節點本身並不是異步的,它只是使用事件循環作爲主要構造。與其他編程語言一樣,事件循環的迭代也是同步執行的。

你的例子在這裏根本不使用異步代碼。只是因爲回調中的某些內容並不一定意味着它是異步的(否則map將是異步的)。你只是在這裏使用高階函數。

試着把這些都放在個人setTimeout s;調用順序不能得到保證。

節點保證運行至完成(即任何功能將被完全執行,除非它throws直到第return語句),所以任何同步代碼將在編寫程序的順序執行 - 就像任何其他勢在必行語言。任何I/O操作或諸如使用Promise之類的東西都會將其回調添加到任務隊列中,以便將來在某個時間點執行,因此不保證其執行順序。

請注意,NodeJS是單線程的,大的for循環會吃掉那個單線程,因爲它是一個CPU綁定操作,所以在做這樣的計算量很大的事情時要小心,因爲您會掛起整個應用程序。對於計算量較大的東西,您可能會屈從於使用另一種更適合此類事物的語言編寫的子進程(使用child_process模塊)。

3

當我打電話給call.countless()它正在執行那個函數,但是沒有任何阻塞I/O的內部。所以Runtime正在忙於循環操作。如果你寫了任何Blocking I/O操作,那麼你會看到NODE JS的異步特性。

例如,阻塞I/O操作:文件讀/寫,超時DB操作,Ajax調用

後for循環已完成,然後翻譯去第二功能。

+0

我認爲你的意思是非阻塞I/O。阻止I/O對Node非常不利。 –

+0

這就是美在哪裏,因爲上面是阻塞I/O,V8引擎會把它放在Event循環中,所以Node單線程將從這些阻塞I/O中解放出來,並且它會繼續執行其他操作@DanPantry –

+0

呃...不,它不會阻止I/O。按照定義阻塞I/O是當前活動的線程將暫停,直到I/O操作完成。節點不會默認執行此操作(但可以通過'module.xxxxSync'操作來執行此操作)。除非使用這些變體,否則所有標準庫節點I/O函數都是非阻塞的。 V8並不像你聲稱的那樣神奇。 –