摘要:實(shí)現(xiàn)方式其實(shí)就是對(duì)方法做了一層封裝,將一個(gè)封裝好的代替?zhèn)鬟f給方法內(nèi)部會(huì)執(zhí)行一次監(jiān)聽回調(diào)函數(shù),然后再調(diào)用對(duì)該回調(diào)進(jìn)行刪除。
事件驅(qū)動(dòng)
Node.js 是一個(gè)基于 Chrome V8 引擎的 JavaScript 運(yùn)行環(huán)境。Node.js 使用了一個(gè)事件驅(qū)動(dòng)、非阻塞式 I/O的模型,使其輕量又高效。Allows you to build scalable network applications usingJavaScript on the server-side.
對(duì)于上面這段官方的引用大家應(yīng)該都看過,nodejs是基于事件驅(qū)動(dòng)和非阻塞I/O的方式來設(shè)計(jì)運(yùn)行的,那么作為實(shí)現(xiàn)事件驅(qū)動(dòng)的核心模塊Events就成了深入學(xué)習(xí)node.js的關(guān)鍵。在node中大部分的模塊的實(shí)現(xiàn)都繼承了Events類。 比如,文件操作中的fs事件流,網(wǎng)絡(luò)編程所用到的tcp,http模塊等,當(dāng)你回想自己寫的程序后,會(huì)發(fā)現(xiàn)很多操作都基于事件驅(qū)動(dòng),Events類。
簡(jiǎn)單來說,就是通過監(jiān)聽事件的狀態(tài)變化來做出相應(yīng)的操作。比如讀取一個(gè)文件,文件讀取完畢,或者文件讀取錯(cuò)誤,那么就觸發(fā)對(duì)應(yīng)的狀態(tài),然后調(diào)用對(duì)應(yīng)的回掉函數(shù)來進(jìn)行處理。
我們來簡(jiǎn)單的看幾段代碼來回憶一下:
const fs = require("fs"); let rs = fs.createReadStream("1.txt"); // 監(jiān)聽文件打開操作 rs.on("open", function() { console.log("open"); }); // 監(jiān)聽數(shù)據(jù)流讀取 rs.on("data", function(data) { console.log(data); }); // 監(jiān)聽錯(cuò)誤 rs.on("error", function() { console.log("error"); }); // 監(jiān)聽讀取結(jié)束操作 rs.on("end", function() { console.log("end"); }); // 監(jiān)聽文件關(guān)閉操作 rs.on("close", function() { console.log("close"); });
上面這段在創(chuàng)建文件讀取流的操作上,針對(duì)文件的打開,數(shù)據(jù),錯(cuò)誤,結(jié)束,關(guān)閉等幾個(gè)狀態(tài)進(jìn)行了監(jiān)聽的回調(diào)處理,這也應(yīng)征了我們上面的定義通過監(jiān)聽事件的狀態(tài)變化來做出相應(yīng)的操作。
那么這些監(jiān)聽事件又是如何觸發(fā)的呢?它是通過Events類中的emit方法去發(fā)射事件的。那么下面我們來看一下Events是如何實(shí)現(xiàn)這樣的監(jiān)聽操作的。
on(eventName, listener)和emitter.addListener(eventName, listener):對(duì)指定事件綁定事件處理函數(shù)
once(eventName, listener):對(duì)指定事件指定只執(zhí)行一次的事件處理函數(shù)
emit(eventName[, ...args]): 觸發(fā)指定事件
removeListener(eventName, listener):對(duì)指定事件解除事件處理函數(shù)
removeAllListeners([event]):對(duì)指定的事件接觸所有的事件處理函數(shù)
setMaxListeners 設(shè)置最大隊(duì)列的長(zhǎng)度
下面來看一下代碼,看看是怎么使用的
const events = require("events"); const EventsEmitter = new events(); //===============事件監(jiān)聽部分=============== EventsEmitter.on("open", function() { console.log("open"); }); EventsEmitter.on("data", function(data) { console.log(data); }); EventsEmitter.on("error", function() { console.log("error"); }); EventsEmitter.on("end", function() { console.log("end"); }); EventsEmitter.on("close", function() { console.log("close"); }); //=============事件觸發(fā)部分================= // 觸發(fā)open事件 EventsEmitter.emit("open"); // 觸發(fā)data事件,并傳遞一個(gè)字符串參數(shù)"test" EventsEmitter.emit("data","test"); // 觸發(fā)error事件 EventsEmitter.emit("error"); // 觸發(fā)end事件 EventsEmitter.emit("end"); // 觸發(fā)close事件 EventsEmitter.emit("close");
看完上面這段代碼是不是更進(jìn)一步的理解了呢。我們回顧最最上面的那段文件流監(jiān)聽的代碼,其實(shí)就是文件在不同的狀態(tài)下去發(fā)射相應(yīng)的emit事件。 而在那段代碼中我們并沒有去引入events這個(gè)node提供的模塊,是因?yàn)槲募髦欣^承了events模塊,所以rs這個(gè)變量也就擁有了相應(yīng)的rs.on()這個(gè)方法了。
看到這里我想應(yīng)該都了解的差不多了。那么下面來試著實(shí)現(xiàn)一下Events這個(gè)類,加深理解。
初始化Events模塊創(chuàng)建一個(gè)Events類
初始化this.events用來保存我們需要監(jiān)聽的事件
將模塊導(dǎo)出
class Events { constructor() { this.events = {}; } } module.exports = Events;實(shí)現(xiàn)Events.on方法
on方法接收兩個(gè)參數(shù):
type:監(jiān)聽的事件類型
listener:回調(diào)函數(shù)
將對(duì)應(yīng)的事件先存放在一個(gè)對(duì)象中,分兩種情況:
該事件對(duì)象不存在,那么以type為key,[listener]為值存放在實(shí)現(xiàn)初始化好的this.events對(duì)象中(注意這里存的是一個(gè)數(shù)組,例如data事件,this.events = {data:[callback]})
如果該事件已經(jīng)存在則直接push
監(jiān)聽函數(shù)就這么簡(jiǎn)單的實(shí)現(xiàn)了,接下來就是等著被emit觸發(fā)了。
/** * 事件監(jiān)聽 * @param {*} type 監(jiān)聽的事件類型 * @param {*} listener 回調(diào)函數(shù) */ on(type, listener) { if (this.events[type]) { this.events[type].push(listener); } else { this.events[type] = [listener]; } }
這里在補(bǔ)充一下,同一個(gè)監(jiān)聽事件是可以添加多個(gè)的,所以這里才會(huì)this.events[type]才會(huì)給一個(gè)數(shù)組來存儲(chǔ)
實(shí)現(xiàn)Events.emit方法
接收兩個(gè)參數(shù):
type:要觸發(fā)的事件類型
...rest:若干個(gè)參數(shù),傳遞給對(duì)應(yīng)事件的回調(diào)函數(shù)
通過type,在this.events里找到相應(yīng)的事件,這里我們上面是存成了一個(gè)數(shù)組,里面對(duì)應(yīng)的是事件的回調(diào)好書。
循環(huán)數(shù)組,執(zhí)行所有對(duì)應(yīng)事件的回調(diào)。
/** * 事件觸發(fā) * @param {*} type 要觸發(fā)的事件類型 * @param {...any} rest 接收到的若干個(gè)參數(shù),這個(gè)參數(shù)會(huì)作為參數(shù)被傳遞到對(duì)應(yīng)事件的回調(diào)函數(shù)中 */ emit(type, ...rest) { if (this.events[type]) { this.events[type].forEach(listener => { listener.apply(this, rest); }); } }
寫到這里,我們之前的代碼就能夠引入自己寫的這個(gè)Events模塊來執(zhí)行了
實(shí)現(xiàn)Events.removeListener方法這個(gè)方法是用來刪除對(duì)應(yīng)事件的某個(gè)監(jiān)聽函數(shù),那么我們只需要把該事件從this.events[type]中刪除即可
接收兩個(gè)參數(shù):
type:事件類型
listener:要?jiǎng)h除的監(jiān)聽函數(shù)
通過this.events[type]找到對(duì)應(yīng)的事件監(jiān)聽函數(shù)數(shù)組
通過filter對(duì)要?jiǎng)h除的監(jiān)聽函數(shù)進(jìn)行過濾
/** * 刪除指定事件中的監(jiān)聽函數(shù) * @param {*} type 對(duì)應(yīng)的事件 * @param {*} listener 要?jiǎng)h除的監(jiān)聽函數(shù) */ removeListener(type, listener) { if (this.events[type]) { this.events[type].filter(l => l !== listener); } }實(shí)現(xiàn)Events.once方法
這個(gè)方法和on一樣,唯一的區(qū)別就是它只會(huì)執(zhí)行一次,即便多次調(diào)用emit去觸發(fā)相同的事件監(jiān)聽,它也只會(huì)執(zhí)行一次。
實(shí)現(xiàn)方式其實(shí)就是對(duì)on()方法做了一層封裝,將一個(gè)封裝好的wraper代替listener傳遞給on()方法
wraper內(nèi)部會(huì)執(zhí)行一次監(jiān)聽回調(diào)函數(shù),然后再調(diào)用this.removeListern對(duì)該回調(diào)進(jìn)行刪除。
/** * 事件監(jiān)聽,但是只執(zhí)行一次 * @param {*} type 監(jiān)聽的事件類型 * @param {*} listener 回調(diào)函數(shù) */ once(type, listener) { const wraper = (...rest) => { listener.apply(this, rest); this.removeListener(type, wraper); }; this.on(type, wrapper); }完整代碼
class Events { constructor() { this.events = {}; } /** * 事件監(jiān)聽 * @param {*} type 監(jiān)聽的事件類型 * @param {*} listener 回調(diào)函數(shù) */ on(type, listener) { if (this.events[type]) { this.events[type].push(listener); } else { this.events[type] = [listener]; } } /** * 事件監(jiān)聽,但是只執(zhí)行一次 * @param {*} type 監(jiān)聽的事件類型 * @param {*} listener 回調(diào)函數(shù) */ once(type, listener) { const wraper = (...rest) => { listener.apply(this, rest); this.removeListener(type, wraper); }; this.on(type, wrapper); } /** * 事件觸發(fā) * @param {*} type 要觸發(fā)的事件類型 * @param {...any} rest 接收到的若干個(gè)參數(shù),這個(gè)參數(shù)會(huì)作為參數(shù)被傳遞到對(duì)應(yīng)事件的回調(diào)函數(shù)中 */ emit(type, ...rest) { if (this.events[type]) { this.events[type].forEach(listener => { listener.apply(this, rest); }); } } /** * 刪除指定事件中的監(jiān)聽函數(shù) * @param {*} type 對(duì)應(yīng)的事件 * @param {*} listener 要?jiǎng)h除的監(jiān)聽函數(shù) */ removeListener(type, listener) { if (this.events[type]) { this.events[type].filter(l => l !== listener); } } } module.exports = Events;總結(jié)
Events模塊是node非常核心的模塊,它對(duì)你深入去學(xué)習(xí)node有很大的幫助,上面我只寫了幾個(gè)方法,內(nèi)部還有幾個(gè)API以及一些非常細(xì)節(jié)的地方可以自己試著去擴(kuò)展,我這里就不一個(gè)一個(gè)的去寫了,文章有寫不好的地方或者看不懂的地方都可以給我留言哦。
如果覺得寫的還行,方便的話幫忙點(diǎn)個(gè)贊哦,謝謝了。
以下我的新個(gè)人微信公眾號(hào),在里面也會(huì)為大家提供原創(chuàng)文章,歡迎大家關(guān)注,當(dāng)關(guān)注用戶量夠了,我會(huì)在里面推出一些視頻教程
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/108980.html
摘要:為指定事件注冊(cè)一個(gè)單次監(jiān)聽器,即監(jiān)聽器最多只會(huì)觸發(fā)一次,觸發(fā)后立刻解除該監(jiān)聽器。移除指定事件的某個(gè)監(jiān)聽器,監(jiān)聽器必須是該事件已經(jīng)注冊(cè)過的監(jiān)聽器。返回指定事件的監(jiān)聽器數(shù)組。如何創(chuàng)建空對(duì)象我們已經(jīng)了解到,是要來儲(chǔ)存監(jiān)聽事件監(jiān)聽器數(shù)組的。 毫無疑問,nodeJS改變了整個(gè)前端開發(fā)生態(tài)。本文通過分析nodeJS當(dāng)中events模塊源碼,由淺入深,動(dòng)手實(shí)現(xiàn)了屬于自己的ES6事件觀察者系統(tǒng)。千萬不...
摘要:深入淺出一直想致力于寫一篇關(guān)于廣義講解系統(tǒng)的文章,苦于時(shí)間有限,資源有限。事件驅(qū)動(dòng)機(jī)制是通過內(nèi)部單線程高效率地維護(hù)事件循環(huán)隊(duì)列來實(shí)現(xiàn)的,沒有多線程的資源占用和上下文的切換。 深入淺出Node.js 一直想致力于寫一篇關(guān)于廣義講解Node.js系統(tǒng)的文章,苦于時(shí)間有限,資源有限。這篇文章是在結(jié)合自己的學(xué)習(xí)心得以及與行業(yè)大佬共同探討下爭(zhēng)對(duì)于熟練掌握J(rèn)S語言后的廣義Node.js.至于為什么...
摘要:深入淺出一直想致力于寫一篇關(guān)于廣義講解系統(tǒng)的文章,苦于時(shí)間有限,資源有限。事件驅(qū)動(dòng)機(jī)制是通過內(nèi)部單線程高效率地維護(hù)事件循環(huán)隊(duì)列來實(shí)現(xiàn)的,沒有多線程的資源占用和上下文的切換。 深入淺出Node.js 一直想致力于寫一篇關(guān)于廣義講解Node.js系統(tǒng)的文章,苦于時(shí)間有限,資源有限。這篇文章是在結(jié)合自己的學(xué)習(xí)心得以及與行業(yè)大佬共同探討下爭(zhēng)對(duì)于熟練掌握J(rèn)S語言后的廣義Node.js.至于為什么...
摘要:深入淺出一直想致力于寫一篇關(guān)于廣義講解系統(tǒng)的文章,苦于時(shí)間有限,資源有限。事件驅(qū)動(dòng)機(jī)制是通過內(nèi)部單線程高效率地維護(hù)事件循環(huán)隊(duì)列來實(shí)現(xiàn)的,沒有多線程的資源占用和上下文的切換。 深入淺出Node.js 一直想致力于寫一篇關(guān)于廣義講解Node.js系統(tǒng)的文章,苦于時(shí)間有限,資源有限。這篇文章是在結(jié)合自己的學(xué)習(xí)心得以及與行業(yè)大佬共同探討下爭(zhēng)對(duì)于熟練掌握J(rèn)S語言后的廣義Node.js.至于為什么...
閱讀 3021·2021-11-24 10:32
閱讀 678·2021-11-24 10:19
閱讀 5069·2021-08-11 11:17
閱讀 1456·2019-08-26 13:31
閱讀 1259·2019-08-23 15:15
閱讀 2287·2019-08-23 14:46
閱讀 2265·2019-08-23 14:07
閱讀 1074·2019-08-23 14:03