搜尋
首頁web前端js教程Node.js中的事件監聽和事件發布用法實例詳解

Node.js中的事件監聽和事件發布用法實例詳解

Jul 24, 2017 am 10:29 AM
javascriptnode.js監聽

node.js是基於單執行緒無阻塞非同步式的I/O,非同步式的I/O指的是當遇到I/O操作的時候,執行緒不阻塞而是進行下面的操作,那麼I/O操作完成之後,執行緒時如何知道該操作完成的呢?

當操作完成耗時的I/O操作之後,會以事件的形式通知I/O操作的執行緒完成,執行緒會在特定的時候來處理這個事件,進行下一步的操作,為了完成非同步I/O,執行緒必須有事件循環的機制,不停的堅持是否有沒有完成的事件,依序完成這些事件的處理。

而對於阻塞式I/O,執行緒遇到耗時的I/O操作會停止繼續執行,等待操作的完成,這個時候執行緒就不能接受其他的操作請求,為了提供吞吐量,必須建立多個線程,每個線程去回應一個客戶的請求,但是同一時間,一個cpu核心上面只能運行一個線程,多個線程要想執行就必須在不同的線程之間進行切換。

因此node.js少了多線程中線程的創建,以及線程的切換的開銷,線程切換的代價是非常大的,需要為其分配內存,列入調度,同時在線程切換的時候需要執行記憶體換頁等等操作,採用單執行緒的方式就可以減少這些操作。但是這種程式設計方式也有缺點,不符合人們的設計思維。

node.js是基於事件的模式來實現非同步I/O的,當其啟動之後會不停的遍歷是否有為完成的事件,然後進行執行,執行完成之後會以另外一個事件的形式通知線程,本操作已經完成,這個事件又會被添加到未完成的事件列表中,線程在接下來的某個時刻遍歷到這個事件然後進行執行,在這種機制中,需要將一個大的任務分成一個小的事件,node.js也適合處理一些高I/O,低邏輯的場景。

下面的範例示範非同步的檔案讀取:


#
var fs = require('fs'); 
fs.readFile('file.txt', 'utf-8', function(err, data) { 
if (err) { 
<span style="white-space:pre"> </span>console.error(err); 
} else { 
<span style="white-space:pre"> </span>console.log(data); 
} 
}); 
[javascript] view plain copy
console.log("end");

如上fs. readFile非同步讀取文件,之後流程就會繼續走,並不會等待其讀取完文件,當文件讀取完畢之後,會發布一個事件,執行線程遍歷到該事件就會去執行對應的操作,這裡是執行對應的回呼函數,例子中字串end會比檔案內容先列印出來。

node.js的事件API

events.EventEmitter:EventEmitter對node.js中的事件發射與事件監聽功能提供了封裝,每個事件由一個標識事件名的字串和對應的操作組成。

事件的監聽:


#
var events = require("events"); 
var emitter = new events.EventEmitter(); 
 <span style="font-family: Arial, Helvetica, sans-serif;">emitter.on("eventName", function(){</span> 
  console.log("eventName事件发生") 
})

#事件的發布:


emitter.emit("eventName");

發布事件的時候我們可以傳入多個參數,第一個參數表示事件的名稱,其後的參數表示傳入的參數,這些參數會被傳入到事件的回調函數中。

EventEmitter.once("eventName", listener) :為事件註冊一個只執行一次的監聽器,當事件第一次發生並觸發監聽器之後,該監聽器就會解除,之後如果事件發生,則該監聽器不會執行。

EventEmitter.removeListener(event, listener) :移除事件的監聽器

EventEmitter.removeAllListeners(event) :移除掉事件的所有的監聽器

EventEmitter.setMaxListeners(n) :node.js預設單一事件最大的監聽器個數是10,如果超過10會給予警告,這麼做是為了防止記憶體的溢出,我們可以改變這個限制設定為其他的數字,如果設定為0表示不進行限制。

EventEmitter.listeners(event) :傳回某個事件的監聽器清單

多重事件之間協作
在稍微大一點的應用程式中,資料與Web伺服器之間的分離是必然的,例如新浪微博、Facebook、Twitter等。這樣的優勢在於資料來源統一,並且可以為相同資料來源製定各種豐富的客戶端程式。

以Web應用為例,在渲染一張頁面的時候,通常需要從多個資料來源拉取數據,並最終渲染至客戶端。 Node.js在這種場景中可以很自然很方便的同時並行發起對多個資料來源的請求。


api.getUser("username", function (profile) {
 // Got the profile
});
api.getTimeline("username", function (timeline) {
 // Got the timeline
});
api.getSkin("username", function (skin) {
 // Got the skin
});

Node.js透過非同步機制使請求之間無阻塞,達到平行請求的目的,有效的呼叫下層資源。但是,這個場景中的問題是對於多個事件回應結果的協調並非被Node.js原生優雅地支援。

為了達到三個請求都得到結果後才進行下一個步驟,程式也許會被變成以下情況:


api.getUser("username", function (profile) {
 api.getTimeline("username", function (timeline) {
  api.getSkin("username", function (skin) {
   // TODO
  });
 });
});

這將導致請求變成串行進行,無法最大化利用底層的API伺服器。

为解决这类问题,我曾写作一个模块来实现多事件协作,以下为上面代码的改进版:


var proxy = new EventProxy();
proxy.all("profile", "timeline", "skin", function (profile, timeline, skin) {
 // TODO
});
api.getUser("username", function (profile) {
 proxy.emit("profile", profile);
});
api.getTimeline("username", function (timeline) {
 proxy.emit("timeline", timeline);
});
api.getSkin("username", function (skin) {
 proxy.emit("skin", skin);
});

EventProxy也是一个简单的事件侦听者模式的实现,由于底层实现跟Node.js的EventEmitter不同,无法合并进Node.js中。但是却提供了比EventEmitter更强大的功能,且API保持与EventEmitter一致,与Node.js的思路保持契合,并可以适用在前端中。
这里的all方法是指侦听完profile、timeline、skin三个方法后,执行回调函数,并将侦听接收到的数据传入。

利用事件队列解决雪崩问题

所谓雪崩问题,是在缓存失效的情景下,大并发高访问量同时涌入数据库中查询,数据库无法同时承受如此大的查询请求,进而往前影响到网站整体响应缓慢。

那么在Node.js中如何应付这种情景呢。


var select = function (callback) {
  db.select("SQL", function (results) {
   callback(results);
  });
 };

以上是一句数据库查询的调用,如果站点刚好启动,这时候缓存中是不存在数据的,而如果访问量巨大,同一句SQL会被发送到数据库中反复查询,影响到服务的整体性能。一个改进是添加一个状态锁。


var status = "ready";
var select = function (callback) {
  if (status === "ready") {
   status = "pending";
   db.select("SQL", function (results) {
    callback(results);
    status = "ready";
   });
  }
 };

但是这种情景,连续的多次调用select发,只有第一次调用是生效的,后续的select是没有数据服务的。所以这个时候引入事件队列吧:


var proxy = new EventProxy();
var status = "ready";
var select = function (callback) {
  proxy.once("selected", callback);
  if (status === "ready") {
   status = "pending";
   db.select("SQL", function (results) {
    proxy.emit("selected", results);
    status = "ready";
   });
  }
 };

这里利用了EventProxy对象的once方法,将所有请求的回调都压入事件队列中,并利用其执行一次就会将监视器移除的特点,保证每一个回调只会被执行一次。对于相同的SQL语句,保证在同一个查询开始到结束的时间中永远只有一次,在这查询期间到来的调用,只需在队列中等待数据就绪即可,节省了重复的数据库调用开销。由于Node.js单线程执行的原因,此处无需担心状态问题。这种方式其实也可以应用到其他远程调用的场景中,即使外部没有缓存策略,也能有效节省重复开销。此处也可以用EventEmitter替代EventProxy,不过可能存在侦听器过多,引发警告,需要调用setMaxListeners(0)移除掉警告,或者设更大的警告阀值。

以上是Node.js中的事件監聽和事件發布用法實例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript的角色:使網絡交互和動態JavaScript的角色:使網絡交互和動態Apr 24, 2025 am 12:12 AM

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C和JavaScript:連接解釋C和JavaScript:連接解釋Apr 23, 2025 am 12:07 AM

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

從網站到應用程序:JavaScript的不同應用從網站到應用程序:JavaScript的不同應用Apr 22, 2025 am 12:02 AM

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python vs. JavaScript:比較用例和應用程序Python vs. JavaScript:比較用例和應用程序Apr 21, 2025 am 12:01 AM

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C/C在JavaScript口譯員和編譯器中的作用C/C在JavaScript口譯員和編譯器中的作用Apr 20, 2025 am 12:01 AM

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

JavaScript在行動中:現實世界中的示例和項目JavaScript在行動中:現實世界中的示例和項目Apr 19, 2025 am 12:13 AM

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

了解JavaScript引擎:實施詳細信息了解JavaScript引擎:實施詳細信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),