標籤:注意 server 進程 命令列 基於 請求 response strong 服務
回調和非同步呼叫的關係
首先明確一點,回調並非是非同步呼叫,回調是一種解決非同步函數執行結果的處理方法。在非同步呼叫,如果我們希望將執行的結果返回並且處理時,可以通過回調的方法解決。為了能夠更好的區分回調和非同步回調的區別,我們來看一個簡單的例子,代碼如下:
function waitFive(name, function_name){ var pus = 0; var currentDate = new Date(); while(pus < 5000){ var now = new Date(); pus = now - currentDate; } function_name(name);}function echo(name){ console.log(name);}waitFive("bob", echo);console.log(‘its over‘);
以上代碼是一個回調邏輯,但不是一個非同步代碼邏輯,因為其中並沒有涉及 Node.js 的非同步呼叫介面。waitFive()函數執行時,整個代碼執行過程都會等待 waitFive() 函數的執行,而並非如非同步呼叫那樣waitFive未結束,還會繼續執行console.log(‘its over’);
因此,回調還是一種阻塞式調用。
非同步函數往往不是直接返回執行結果,而是通過事件驅動方式,將執行結果返回到回呼函數中,之後在回呼函數中處理相應的邏輯代碼。
Node.js中很多API的調用模式是非同步呼叫的,因此在學習Node.js過程中理解非同步呼叫、同步調用和回調是非常重要的。
為什麼非同步函數需要回呼函數?
先看這樣一個例子:
var dns = require(‘dns‘); // require dns 模組var address = dns.resolved4(‘www.baidu.com‘, function(address){});//dns 同步解析console.log(address);
當我們擷取address值時,會出現異常,提示address沒有定義undefined。列印出address,可以看到第二個例子結果為null,原因很簡單,非同步函數dns.resolve4()還未執行結束時,就已經執行到 console.log(address),因此最終 address 為 null。既然非同步函數出現這個問題,我們就可以使用回調去擷取函數。如下代碼,通過回呼函數擷取執行的結果 address 值:
var dns = require(‘dns‘);dns.resolve4(‘www.baidu.com‘, function(address){ console.log(address);})
Node.js —— 基於事件驅動的回調
為什麼它對我們用 Node.js 寫網路應用是具有意義的?
當我們使用 http.createServer 方法的時候,我們當然不只是想要一個偵聽某個連接埠的伺服器,我們還想要它在伺服器收到一個HTTP請求的時候做點什麼。問題是,這是非同步:請求任何時候都可能到達,但是我們的伺服器卻跑在一個單進程中。
寫PHP應用的時候,我們一點也不為此擔心:任何時候當有請求進入的時候,網頁伺服器(通常是Apache)就為這一請求建立一個進程,並且開始從頭到尾執行相應的PHP指令碼。
我們先來看一個基於Node.js簡約而不簡單的HTTP伺服器:
var http = require("http");http.createServer(function(request, response){ response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end();}).listen(8888);
那麼在我們的Node.js程式中,當一個新的請求到達8888連接埠的時候,我們怎麼控制流程程呢?
嗯,這就是Node.js/JavaScript的事件驅動設計能夠真正幫上忙的地方了 —— 雖然我們還得學一些新概念才能掌握它。讓我們來看看這些概念是怎麼應用在我們的伺服器代碼裡的。
我們建立了伺服器,並且向建立它的方法傳遞了一個函數。無論何時我們的伺服器收到一個請求,這個函數就會被調用。我們不知道這件事情什麼時候會發生,但是我們現在有了一個處理請求的地方:它就是我們傳遞過去的那個函數。至於它是被預先定義的函數還是匿名函數,都無關緊要了。這個就是傳說中的 回調(Node.js中的非同步回調)。
讓我們再來琢磨琢磨 [ Node.js中的非同步回調 ] 這個概念。
我們怎麼證明在建立完伺服器之後,即時沒有HTTP請求進來,我們的回呼函數也沒有被調用的情況下,我們的代碼還繼續有效呢?試試這個:
var http = require("http");http.createServer(function(request, response){ console.log("Request received."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end();}).listen(8888);console.log("Server has started.");
注意:在匿名回呼函數觸發的地方,我們用 console.log 輸出了一段文本;在HTTP伺服器開始工作之後,也輸出一段文本。
當我們與往常一樣,運行 node server.js 時,它會馬上在命令列上輸出 “Server has started.”。當我們向伺服器發出請求(在瀏覽器訪問http://localhost:8888),“Request received.”這條訊息就會在命令列中出現。(請注意,當我們在伺服器訪問網頁時,我們的伺服器可能會輸出兩次“Request received.”。那是因為大部分瀏覽器都會在你訪問 http://localhost:8888 時嘗試讀取 http://localhost:8888/favicon.ico) —— 這就是事件驅動的非同步伺服器端 JavaScript 和它的回調啦!
深入理解Node.js基於事件驅動的回調