1. 程式人生 > >【NodeJS】深入理解Node.js基於事件驅動的回撥

【NodeJS】深入理解Node.js基於事件驅動的回撥

回撥和非同步呼叫的關係

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

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

相關推薦

NodeJS深入理解Node.js基於事件驅動

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

node.js 基於事件驅動

基於事件驅動的回撥 當我們使用 http.createServer 方法的時候,我們當然不只是想要一個偵聽某個埠的伺服器,我們還想要它在伺服器收到一個HTTP請求的時候做點什麼。 問題是,這是非同步的:請求任何時候都可能到達,但是我們的伺服器卻跑在一個單程序中。 寫

知識深入理解js閉包

nts 存在 window 依次 ner hat 再看 tex 程序 本文轉載: 一、變量的作用域 要理解閉包,首先必須理解Javascript特殊的變量作用域。 變量的作用域無非就是兩種:全局變量和局部變量。 Javascript語言的特殊之處,就在於函數內部可以直接讀取

淺談Node.js單線程模型

包裝 傳遞參數 銷毀 img lba afr 第一個元素 request 浪費 Node.js采用 事件驅動 和 異步I/O 的方式,實現了一個單線程、高並發的運行時環境,而單線程就意味著同一時間只能做一件事,那麽Node.js如何利用單線程來實現高並發和異步I/O?本文將

深入理解margin

盒模型 mbed 推理 日誌 onf cap limited textarea 效果 由淺入深漫談margin屬性 2007-3-18 上午 - HTML/CSS/XML/XSL - CSS - margin margin 在中文中我們翻譯成外邊距或者外補白(

轉載深入理解 GRE tunnel

我以前寫過一篇 介紹 tunnel 的文章 ,只是做了大體的介紹。裡面多數 tunnel 是很容易理解的,因為它們多是一對一的,換句話說,是直接從一端到另一端。比如 IPv6 over IPv4 的 tunnel,也就是 SIT,它的原理如下圖所示:

web深入理解負載均衡 2018-10-5

深入理解負載均衡 負載均衡 負載均衡是高可用架構的一個關鍵元件,主要用來提高效能和可用性,通過負載均衡將流量分發到多個伺服器,同時多伺服器能夠消除這部分的單點故障。 當然負載均衡器本身就是一個單點故障隱患,可以考慮文章後面說的負載均衡雙機熱備或其他方案消除單點

Netty深入理解ByteBuf

之前的關於【Netty】的文章我們已經瞭解到 Netty 裡面資料讀寫是以 ByteBuf 為單位進行互動的,這一小節,我們就來詳細剖析一下 ByteBuf 之前文章連結: (一)Netty學習前篇 (二)Netty學習前篇 (三)Netty學習前篇 結構 從

深入理解定位父級offsetParent及偏移大小

 偏移量(offset dimension)是javascript中的一個重要的概念。涉及到偏移量的主要是offsetLeft、offsetTop、offsetHeight、offsetWidth這四個屬性。當然,還有一個偏移參照——定位父級offsetParent。本文將詳細介紹該部分內容

深入理解Node.js中的Async和Await函式

這篇文章主要介紹了Node.js中的Async和Await函式的相關知識,非常不錯,寫的十分的全面細緻,具有一定的參考價值,對此有需要的朋友可以參考學習下。如有不足之處,歡迎批評指正。 在本文中,你將學習如何使用Node.js中的async函式(async/await)來簡化c

Linux深入理解HTTP協議

本文參考自: 原文地址 基本概念及作用 首先HTTP協議屬於應用層協議,應用層協議就是我們程式設計師自己定協議,但如果每次都自己定麻煩並且很容易出錯,所以有了一些現成的(HTTP,DNS)等,所以大多是直接拿來用就行,而應用層的作用就是雙方達成某種約定,一段按照約

C++深入理解“內聯與巨集”

行內函數 內聯程式碼,程式無需跳到另一個位置執行程式碼,再跳回來。因此,行內函數執行速度比常規函式稍快,但代價是需要佔用更多記憶體。 所以應該有選擇性的使用行內函數,如果函式執行程式碼的時間比處理函式呼叫的 時間長,則即使使用行內函數,節省也沒啥明顯改進,而如果程式碼執行時間很短,則行內函數

轉載 深入理解WeakHashmap

轉載出處 WeakHashmap (一) 檢視API文件,WeakHashmap要點如下: 以弱鍵 實現的基於雜湊表的 Map。在 WeakHashMap 中,當某個鍵不再正常使用時,將自動移除其條目。更精確地說,對於一個給定的鍵,其對映的存在並不阻止垃

深入理解Node.js垃圾回收與內存管理

idt ole ryu 占用 出現 and 命令 var 回退 使用JavaScript進行前端開發時幾乎完全不需要關心內存管理問題,對於前端編程來說,V8限制的內存幾乎不會出現用完的情況,但是由於後端程序往往進行的操作更加復雜,並且長期運行在服務器不重啟,如果不關註內存管

C++深入理解模板

1、簡介 模板是一種程式碼複用方式,其它的程式碼複用方式還包括繼承和組合。當我們使用模板時,引數由編譯器來替換,這非常像原來的巨集方法,但卻更清晰、更容易使用。在C++中,模板實現了引數化型別的概念,放在一對尖括號中,通過template這個關鍵字,告訴編譯器

深入理解C++的動態繫結和靜態繫結

為了支援c++的多型性,才用了動態繫結和靜態繫結。理解他們的區別有助於更好的理解多型性,以及在程式設計的過程中避免犯錯誤。 需要理解四個名詞: 1、物件的靜態型別:物件在宣告時採用的型別。是在編譯期確定的。 2、物件的動態型別:目前所指物件的型別。是在執行期決定的。物件的

深入理解JDBC的超時設定

恰當的JDBC超時設定能夠有效地減少服務失效的時間。本文將對資料庫的各種超時設定及其設定方法做介紹。真實案例:應用伺服器在遭到DDos攻擊後無法響應在遭到DDos攻擊後,整個服務都垮掉了。由於第四層交換機不堪重負,網路變得無法連線,從而導致業務系統也無法正常運轉。安全組很快遮蔽了所有的DDos攻擊,並恢復了網

nodejs---關於真正理解Node.js事件迴圈你需要了解的一切

Node.js是一個基於事件的平臺。這意味著Node中發生的任何事情都是對於事件的響應。傳入Node的資料處理要經歷一層層巢狀的回撥。這一流程相對於開發者被抽象出來,由一個叫做libuv的庫處理,就是libuv為我們提供了事件迴圈機制。事件迴圈也許是Node中最容易被誤解的概

OO深入理解ABAP Object Step by Step (二)

第一篇step by step 整理的是如何建立類,並生成類的物件來呼叫類的方法,這一邊著重理解類與類的關係。 1,繼承。 類所具有的特徵: A.類是可以被繼承 B.所有類都是從一個OBJECT的類繼承下來的 C.如果一個類沒有明確表示繼承於某個類,那它就是從OBJECT

深入理解Linux的系統呼叫

  一、 什麼是系統呼叫      在Linux的世界裡,我們經常會遇到系統呼叫這一術語,所謂系統呼叫,就是核心提供的、功能十分強大的一系列的函式。這些系統呼叫是在核心中實現的,再通過一定的方式把系統呼叫給使用者,一般都通過門(gate)陷入(trap)實現。系統呼叫是使用