回撥和非同步呼叫的關係

  首先明確一點,回撥並非是非同步呼叫,回撥是一種解決非同步函式執行結果的處理方法。在非同步呼叫,如果我們希望將執行的結果返回並且處理時,可以通過回撥的方法解決。為了能夠更好的區分回撥和非同步回撥的區別,我們來看一個簡單的例子,程式碼如下:

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 和它的回撥啦!
這裡寫圖片描述
當我們再次重新整理後
這裡寫圖片描述

.