(八)nodejs循序漸進-事件驅動(進階篇)

事件驅動程序

Node.js 使用事件驅動模型,當web server接收到請求,就把它關閉然后進行處理,然后去服務下一個web請求。

當這個請求完成,它被放回處理隊列,當到達隊列開頭,這個結果被返回給用戶。

這個模型非常高效可擴展性非常強,因為 webserver 一直接受請求而不等待任何讀寫操作。(這也稱之為非阻塞式IO或者事件驅動IO)

在事件驅動模型中,會生成一個主循環來監聽事件,當檢測到事件時觸發回調函數。

?

整個事件驅動的流程就是這么實現的,非常簡潔。有點類似于觀察者模式,事件相當于一個主題(Subject),而所有注冊到這個事件上的處理函數相當于觀察者(Observer)。

?EventEmitter觸發器

大多數 Node.js 核心 API 構建于慣用的異步事件驅動架構,其中某些類型的對象(又稱觸發器,Emitter)會觸發命名事件來調用函數(又稱監聽器,Listener)。

例如??net.Server?會在每次有新連接時觸發事件 fs.ReadStream會在打開文件時觸發事件,stream會在數據可讀時觸發事件。

所有能觸發事件的對象都是?EventEmitter?類的實例。 這些對象有一個?eventEmitter.on()?函數,用于將一個或多個函數綁定到命名事件上。。

當?EventEmitter?對象觸發一個事件時,所有綁定在該事件上的函數都會被同步地調用。 被調用的監聽器返回的任何值都將會被忽略并丟棄。

一個簡單的?EventEmitter?實例,綁定了一個監聽器。?eventEmitter.on()?用于注冊監聽器,?eventEmitter.emit()?用于觸發事件。

Node.js 有多個內置的事件,我們可以通過引入 events 模塊,并通過實例化 EventEmitter 類來綁定和監聽事件,如下實例:

const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.on('event', () => {console.log('觸發事件');
});
myEmitter.emit('event');

?

將參數和 this 傳給監聽器??

eventEmitter.emit()?方法可以傳任意數量的參數到監聽器函數。 當監聽器函數被調用時,?this?關鍵詞會被指向監聽器所綁定的?EventEmitter?實例。

const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.on('event', function(a, b) {console.log(a, b, this, this === myEmitter); 
});
myEmitter.emit('event', 'a', 'b');

也可以使用 ES6 的箭頭函數作為監聽器。但?this?關鍵詞不會指向?EventEmitter?實例:?

const events = require('events');const myEmitter = new events.EventEmitter(); 
myEmitter.on('event', (a, b) => {console.log(a, b, this, this ,this===myEmitter); 
});
myEmitter.emit('event', 'a', 'b');

結果輸出:

a b {} {} false?

異步 VS 同步

EventEmitter?以注冊的順序同步地調用所有監聽器。 這樣可以確保事件的正確排序,并有助于避免競態條件和邏輯錯誤。 當適當時,監聽器函數可以使用?setImmediate()?和?process.nextTick()?方法切換到異步的操作模式:

const events = require('events');const myEmitter = new events.EventEmitter(); 
myEmitter.on('event', (a, b) => {setImmediate(() => {console.log('異步地發生');});
});
myEmitter.emit('event', 'a', 'b');

僅處理事件一次

當使用?eventEmitter.on()?注冊監聽器時,監聽器會在每次觸發命名事件時被調用。

const events = require('events');const myEmitter = new events.EventEmitter(); 
let m = 0;
myEmitter.on('event', () => {console.log(++m);
});
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
// 打印: 2

使用?eventEmitter.once()?可以注冊最多可調用一次的監聽器。 當事件被觸發時,監聽器會被注銷,然后再調用。

const events = require('events');const myEmitter = new events.EventEmitter(); 
let m = 0;
myEmitter.once('event', () => {console.log(++m);
});
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
// 不觸發

錯誤事件

當?EventEmitter?實例出錯時,應該觸發?'error'?事件。 這些在 Node.js 中被視為特殊情況。

如果沒有為?'error'?事件注冊監聽器,則當?'error'?事件觸發時,會拋出錯誤、打印堆棧跟蹤、并退出 Node.js 進程。

const events = require('events');const myEmitter = new events.EventEmitter(); 
myEmitter.emit('error', new Error('錯誤信息'));
// 拋出錯誤并使 Node.js 崩潰。

為了防止崩潰 Node.js 進程,通過使用符號?errorMonitor?安裝監聽器,可以監視?'error'?事件但不消耗觸發的錯誤。

const events = require('events');const myEmitter = new events.EventEmitter(); 
myEmitter.on(EventEmitter.errorMonitor, (err) => {MyMonitoringTool.log(err);
});
myEmitter.emit('error', new Error('錯誤'));
// 仍然拋出錯誤并使 Node.js 崩潰。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/444543.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/444543.shtml
英文地址,請注明出處:http://en.pswp.cn/news/444543.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

leetcode304. 二維區域和檢索 - 矩陣不可變

給定一個二維矩陣,計算其子矩形范圍內元素的總和,該子矩陣的左上角為 (row1, col1) ,右下角為 (row2, col2)。 上圖子矩陣左上角 (row1, col1) (2, 1) ,右下角(row2, col2) (4, 3),該子矩形內元素的總和為 8。 示例…

(九)nodejs循序漸進-Express框架(進階篇)

Express 框架 Express 是一個簡潔而靈活的 node.js Web應用框架, 提供了一系列強大特性幫助你創建各種 Web 應用,和豐富的 HTTP 工具。 使用 Express 可以快速地搭建一個完整功能的網站。 Express 框架核心特性: 可以設置中間件來響應 HTTP 請求。 定…

leetcode326. 3的冪 如此6的操作你想到了嗎

給定一個整數,寫一個函數來判斷它是否是 3 的冪次方。 示例 1: 輸入: 27 輸出: true 示例 2: 輸入: 0 輸出: false 示例 3: 輸入: 9 輸出: true 示例 4: 輸入: 45 輸出: false 進階: 你能不使用循環或者遞歸來完成本題嗎? 注意最后一句…

(十)nodejs循序漸進-高性能游戲服務器框架pomelo之介紹和安裝篇

目錄 Pomelo 安裝Pomelo 創建demoserver項目 pomelo命令 項目結構說明 pomelo框架 架構 服務器實現 客戶端請求與響應、廣播的抽象介紹 Pomelo pomelo是一個快速、可擴展、Node.js分布式游戲服務器框架,對游戲服務器開發感興趣的同學可以關注關注。 之前…

leetcode344. 反轉字符串 史上最簡單力扣題

編寫一個函數,其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 char[] 的形式給出。 不要給另外的數組分配額外的空間,你必須原地修改輸入數組、使用 O(1) 的額外空間解決這一問題。 你可以假設數組中的所有字符都是 ASCII 碼表中的可打印字符。…

(十一)nodejs循序漸進-高性能游戲服務器框架pomelo之啟動流程和組件

游戲啟動過程 啟動入口 在使用pomelo進行游戲開發時,工程目錄下的app.js是整個游戲服務器的啟動運行入口。app.js中創建項目,進行默認配置并啟動服務器的代碼如下: var pomelo require(pomelo); var app pomelo.createApp(); app.set(na…

(十二)nodejs循序漸進-高性能游戲服務器框架pomelo之創建一個游戲聊天服務器

上個章節我們簡單介紹了下pomelo的安裝和目錄結構,有讀者可能覺得有點吃不消,為什么不再深入講一講目錄結構和里邊的庫,這里我就不費口舌了,大家可以去官網參考文檔說明,本文只告訴大家如何利用這個框架來開發自己的東…

看這玩意復習你還會掛科?《軟件工程篇》

軟件工程:是指導軟件開發和維護的一門工程學科 三要素方法/工具/開發過程 價值:促進項目成功 現代產品開發三原則:功用性、可行性、稱許性 軟件過程是軟件工程的核心組成部分。 迭代 :反復求精 增量:逐塊建造 需…

C++:02---命名空間

一、概念: ①類似于倉庫,空間內存儲代碼,需要用到時調用②也為防止名字沖突提供了更加可控的機制二、命名空間的定義 定義的基本格式如下:namespace 命名空間名 { //一系列聲明與定義 };三、命名空間的注意事項 命名空間定義時最后的分號可有可無只要出現在全局作用域中的…

看這玩意復習你還會掛科?《軟件工程2篇》

第一章: 軟件工程定義: 1968年10月,Fritz Bauer 首次提出了“軟件工程”的概念,并將“軟件工程”定義為:為了經濟地獲得能夠在實際機器上有效運行的可靠軟件,而建立并使用的一系列工程化原則。 1993年IE…

C++:05---命名空間

一、概念: ①類似于倉庫,空間內存儲代碼,需要用到時調用②也為防止名字沖突提供了更加可控的機制二、命名空間的定義 定義的基本格式如下:namespace 命名空間名 { //一系列聲明與定義 };三、命名空間的注意事項 命名空間定義時最后的分號可有可無只要出現在全局作用域中的…

C++:04---內聯函數

1.概念: 內聯類似于宏定義,當程序執行到內聯函數時,相當于復制了一份函數代碼。犧牲代碼空間,贏得了時間 內聯說明只是向編譯器發出一個請求,編譯器可以選擇忽略這個請求 2.關鍵字:inline 聲明時寫了inline,定義時可省略。建議聲明和定義都加上inlineinline int add(int…

leetcode86. 分隔鏈表

給定一個鏈表和一個特定值 x,對鏈表進行分隔,使得所有小于 x 的節點都在大于或等于 x 的節點之前。 你應當保留兩個分區中每個節點的初始相對位置。 示例: 輸入: head 1->4->3->2->5->2, x 3 輸出: 1->2->2->4->3->5…

(十三)nodejs循序漸進-高性能游戲服務器框架pomelo之擴展聊天服務器為機器人自動聊天

聊天服務器擴展 大家在上一篇文章里相信已經學會了pomelo框架的基本用法了,那么我們在上一篇文章的代碼基礎上繼續擴展,豐富系統,另外也熟悉下他的更多的用法,這一節我將擴展它:增加一個機器人自動聊天的功能。 目的…

C++:09---類靜態成員、類常量成員

一、類靜態成員(static) 先介紹一下什么是靜態變量、靜態函數 靜態局部變量:存在域(全局數據區),作用域(塊作用域)靜態全局變量:存在域(全局數據區),作用域(整個文件)靜態函數:存在域(全局數據區),作用域(整個文件)static int a=10;//全局靜態變量 static vo…

C++:08---成員變量初始化方式

成員變量初始化有三種方式: 在構造函數體內賦值初始化在自定義的公有函數體中賦值初始化(一般用于成員變量的初始化)在構造函數的成員初始化列表初始化一、構造函數體內初始化 說明:在構造函數體內的初始化方式,本質是是為成員變量賦值,而不是真正意義上的初始化,這點要…

leetcode1290. 二進制鏈表轉整數 刷新認知,最簡單算法題

給你一個單鏈表的引用結點 head。鏈表中每個結點的值不是 0 就是 1。已知此鏈表是一個整數數字的二進制表示形式。 請你返回該鏈表所表示數字的 十進制值 。 示例 1: 輸入:head [1,0,1] 輸出:5 解釋:二進制數 (101) 轉化為十進…

Redis:02---安裝Redis(Linux+Windows+Docker)

Linux安裝:一、安裝方式1(下載源碼編譯安裝)第一步:從下面的網址中下載Redis最新穩定版本的源代碼sudo wget http://download.redis.io/redis-stable.tar.gz第二步:下載完之后解壓,建立一個軟鏈接指向于red…

C++:10---再議拷貝構造函數

一、概念 使用一個已經存在的對象,去構造(初始化)另一個對象二、格式 參數加上const&,因為拷貝構造函數在幾種情況下都會被隱式地使用,因此拷貝構造函數不應該是explict的const:防止函數內部修改值&:防止無限循環拷貝類名(類名 const& 參數名) { 函數體 }三、…

人的思維謬誤與心理學效應

啟發法 用一個容易的問題代替難以回答的真正問題。這個容易的問題的答案就是對真正問題的啟發,但啟發經常和真正的答案差得很遠,而人卻往往把啟發當成了真正問題的答案。 接下來介紹和啟發法相關的心理效應和謬誤。每一個謬誤都會注明真正的問題是什么…