《NodeJS開發(fā)教程8-事件》

事件EventEmitter可謂是NodeJS中的重中之重!
大多數(shù) Node.js 核心 API 都采用慣用的異步事件驅(qū)動(dòng)架構(gòu),其中某些類型的對(duì)象(觸發(fā)器)會(huì)周期性地觸發(fā)命名事件來調(diào)用函數(shù)對(duì)象(監(jiān)聽器)。

例如,net.Server 對(duì)象會(huì)在每次有新連接時(shí)觸發(fā)事件;fs.ReadStream 會(huì)在文件被打開時(shí)觸發(fā)事件;流對(duì)象 會(huì)在數(shù)據(jù)可讀時(shí)觸發(fā)事件。

所有能觸發(fā)事件的對(duì)象都是 EventEmitter 類的實(shí)例。 這些對(duì)象開放了一個(gè) eventEmitter.on() 函數(shù),允許將一個(gè)或多個(gè)函數(shù)綁定到會(huì)被對(duì)象觸發(fā)的命名事件上。 事件名稱通常是駝峰式的字符串,但也可以使用任何有效的 JavaScript 屬性名。

當(dāng) EventEmitter 對(duì)象觸發(fā)一個(gè)事件時(shí),所有綁定在該事件上的函數(shù)都被同步地調(diào)用。 監(jiān)聽器的返回值會(huì)被丟棄。

  • 事件的基本使用
    首先需要引入
/*事件*/
var EventEmitter=require("events");

require("events")返回的EventEmitter是一個(gè)類(或者說events模塊exports導(dǎo)出的是一個(gè)class)然后:

/*事件*/
var EventEmitter=require("events");

const event=new EventEmitter();//創(chuàng)建一個(gè)事件發(fā)射器實(shí)例
//注冊(cè)異步事件
event.on("customEvent",function()
{
    console.log("customEvent事件觸發(fā)");
});

//觸發(fā)事件
event.emit("customEvent");

我們通過 const event=new EventEmitter()得到一個(gè)實(shí)例對(duì)象event
接著可以通過event.on(eventName,callbackfunc)來注冊(cè)一個(gè)異步事件
在需要觸發(fā)eventName事件時(shí)使用 event.emit(eventName)進(jìn)行事件的觸發(fā),事件可以注冊(cè)多個(gè)相同事件的監(jiān)聽器,在觸發(fā)該事件時(shí),依次按照注冊(cè)的順序進(jìn)行觸發(fā)事件監(jiān)聽器的回調(diào)方法callbackfunc。

  • 事件監(jiān)聽器回調(diào)方法中的this
    事件監(jiān)聽器回調(diào)方法中的this,指向的對(duì)象會(huì)有不同:
    在function(){this}中this指向的是觸發(fā)該事件的發(fā)射器實(shí)例對(duì)象,也就是 下面示例中的 myEmitter
    在箭頭函數(shù)()=>{this}中this指向的則是myEmitter所在的父作用域?qū)ο蟆?/li>
class MyEmitter extends EventEmitter{

    constructor(name)
    {
        super();
        this.name=name;
    }
    getName()
    {
        return this.name;
    }

}

const myEmitter=new MyEmitter("自定義事件發(fā)射器");
myEmitter.on("clicktest",function()
{
    //會(huì)打?。篶licktest事件觸發(fā) 自定義事件發(fā)射器
    console.log("clicktest事件觸發(fā)",this.getName());

});
myEmitter.on("clicktest",()=>{
    //箭頭函數(shù)中this指向的并不是myEmitter
    //會(huì)打?。篶licktest事件觸發(fā) myEmitter父作用域中的getName()方法
    //如果把下面的this.getName=function(){}注釋掉,這個(gè)事件觸發(fā)后會(huì)報(bào)錯(cuò),因?yàn)檎也坏絞etName()方法;
    console.log("clicktest事件觸發(fā)",this.getName());
});
this.getName=function()
{
    return "myEmitter父作用域中的getName()方法";
}

//觸發(fā)事件
myEmitter.emit("clicktest");
  • 事件發(fā)生的同步與異步
    EventEmitter 會(huì)按照監(jiān)聽器注冊(cè)的順序同步地調(diào)用所有監(jiān)聽器。 所以需要確保事件的正確排序且避免競(jìng)爭(zhēng)條件或邏輯錯(cuò)誤。 監(jiān)聽器函數(shù)可以使用 setImmediate() 或 process.nextTick() 方法切換到異步操作模式:
//同步與異步事件發(fā)生
myEmitter.on("asyncEvent",function()
{
    setImmediate(()=>{
        console.log("asyncEvent事件發(fā)生是異步的");
    });
    console.log("asyncEvent事件發(fā)生是同步的");
});
myEmitter.emit("asyncEvent");
console.log("看誰(shuí)先執(zhí)行,異步和同步");

執(zhí)行輸出:

asyncEvent事件發(fā)生是同步的
看誰(shuí)先執(zhí)行,異步和同步
asyncEvent事件發(fā)生是異步的
  • 只觸發(fā)一次事件
    我們使用 myEmitter.on()注冊(cè)的事件,每當(dāng)在調(diào)用 myEmitter.emit()時(shí)都會(huì)觸發(fā)一次監(jiān)聽器回調(diào),如果我們需要只讓他觸發(fā)一次監(jiān)聽器回調(diào),應(yīng)該使用
    myEmitter.once()進(jìn)行事件的注冊(cè)
//只執(zhí)行一次監(jiān)聽回調(diào)
myEmitter.on("myevent",function()
{
    console.log("myevent事件觸發(fā)");
});
myEmitter.once("myevent2",function()
{
    console.log("myevent2事件觸發(fā)");
});

myEmitter.emit("myevent");
myEmitter.emit("myevent");

myEmitter.emit("myevent2");
myEmitter.emit("myevent2");

執(zhí)行輸出:

myevent事件觸發(fā)
myevent事件觸發(fā)
myevent2事件觸發(fā)
  • newListener內(nèi)置事件
    myEmitter.on("newListener",function(){});
    newListener事件屬于內(nèi)置事件,含義是:當(dāng) myEmitter實(shí)例對(duì)象有 新事件監(jiān)聽器注冊(cè)時(shí) 觸發(fā)該 newListener事件。
//當(dāng)有事件注冊(cè)監(jiān)聽時(shí)觸發(fā)
myEmitter.on("newListener",function()
{
    console.log("newListener事件觸發(fā)");
});

myEmitter.on("myevent",function()
{
    console.log("myevent事件觸發(fā)");
});
//只執(zhí)行一次監(jiān)聽回調(diào)
myEmitter.once("myevent2",function()
{
    console.log("myevent2事件觸發(fā)");
});

執(zhí)行輸出

newListener事件觸發(fā)
newListener事件觸發(fā)
  • removeListener事件移除
//事件移除
myEmitter.on("genEvent",handlerEvent);

function handlerEvent()
{
    console.log("genEvent事件觸發(fā)");
}

myEmitter.emit("genEvent");
//移除改事件監(jiān)聽后,后續(xù)再emit該事件則忽略不會(huì)觸發(fā)監(jiān)聽回調(diào)
myEmitter.removeListener("genEvent",handlerEvent);
myEmitter.emit("genEvent");

執(zhí)行輸出:

genEvent事件觸發(fā)
  • 最大監(jiān)聽器注冊(cè)數(shù)量
    每個(gè)事件默認(rèn)可以注冊(cè)最多 10 個(gè)監(jiān)聽器。 單個(gè) EventEmitter 實(shí)例的限制可以使用 emitter.setMaxListeners(n) 方法改變

  • emitter.getMaxListeners()獲取最大監(jiān)聽器數(shù)
    返回 EventEmitter 當(dāng)前的最大監(jiān)聽器限制值,該值可以通過 emitter.setMaxListeners(n)

nodejs性能的提升大部分原因是得益于 異步事件機(jī)制。它摒棄掉了多線程,使用單線程進(jìn)行處理主邏輯(當(dāng)然io操作依然是多線程處理)通過異步事件來節(jié)約cpu的等待周期,最大限度的壓榨cpu資源。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Node.js EventEmitter Node.js 所有的異步 I/O 操作在完成時(shí)都會(huì)發(fā)送一個(gè)事件到事件隊(duì)...
    FTOLsXD閱讀 377評(píng)論 1 2
  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測(cè)試 ...
    KeKeMars閱讀 6,603評(píng)論 0 6
  • Module definition patterns 除了作為加載依賴的機(jī)制之外,模塊系統(tǒng)也是一種用于定義AP...
    宮若石閱讀 556評(píng)論 0 0
  • 前言: 因?yàn)橐郧皩W(xué)習(xí)Node.js并沒有真正意義上的去學(xué)習(xí)它,而是粗略的學(xué)習(xí)了npm的常用命令和Node.js一些...
    Srtian閱讀 1,110評(píng)論 1 17
  • 歲月如一幅畫,勾勒在了時(shí)間的空隙處,一筆一劃用曾經(jīng)畫出了現(xiàn)在的山水。歲月如一首歌,綿綿起伏,用悠揚(yáng)的音符,跳躍在人...
    流徒風(fēng)情閱讀 173評(píng)論 0 0

友情鏈接更多精彩內(nèi)容