【原創(chuàng)】Node核心API(二)Events

Events模塊是Node對(duì)“發(fā)布/訂閱”模式(publish/subscribe)的實(shí)現(xiàn),一個(gè)對(duì)象通過(guò)這個(gè)模塊,向另一個(gè)對(duì)象傳遞消息,幾乎所有常用的node模塊都繼承了events模塊,比如http、fs等。
Node中的Event模塊僅僅提供了一個(gè)對(duì)象: EventEmitter, EventEmitter 的核心就是事件觸發(fā)與事件監(jiān)聽(tīng)器功能的封裝。

1、訂閱發(fā)布模式(Subscribe/Publish)

訂閱發(fā)布模式(又稱事件監(jiān)聽(tīng)器模式)廣泛用于異步編程中。events模塊是訂閱發(fā)布模式的一個(gè)簡(jiǎn)單實(shí)現(xiàn)。訂閱發(fā)布模式定義了一種一對(duì)多的依賴關(guān)系,在Node中EventEmitter 對(duì)象上開(kāi)放了一個(gè)可以用于監(jiān)聽(tīng)的on(eventName,callback)函數(shù),允許將一個(gè)或多個(gè)函數(shù)綁定到對(duì)應(yīng)的事件上。當(dāng) EventEmitter 對(duì)象觸發(fā)一個(gè)事件時(shí),所有綁定在該事件上的函數(shù)都被同步地調(diào)用!

2、Events的API

Events API概覽

大多數(shù)時(shí)候我們不會(huì)直接使用 EventEmitter,而是在對(duì)象中繼承它。包括 fs、net、 http 在內(nèi)的,只要是支持事件響應(yīng)的核心模塊都是 EventEmitter 的子類。
原因有兩點(diǎn):

  • 具有某個(gè)實(shí)體功能的對(duì)象實(shí)現(xiàn)事件符合語(yǔ)義, 事件的監(jiān)聽(tīng)和發(fā)射應(yīng)該是一個(gè)對(duì)象的方法。
  • JavaScript 的對(duì)象機(jī)制是基于原型的,支持 部分多重繼承,繼承 EventEmitter 不會(huì)打亂對(duì)象原有的繼承關(guān)系。

3、方法

3.1 創(chuàng)建監(jiān)聽(tīng)器

  • emitter.on(eventName, listener):添加 listener 函數(shù)到名為 eventName 的事件的監(jiān)聽(tīng)器數(shù)組的末尾。 不會(huì)檢查 listener 是否已被添加。 多次調(diào)用并傳入相同的 eventName 與 listener 會(huì)導(dǎo)致 listener 會(huì)被添加多次。
  • emitter.addListener(eventName, listener):emitter.on(eventName, listener)的別名。
//引入events模塊
const EventEmitter = require('events');
let count = 0;
//創(chuàng)建一個(gè)新實(shí)例
const myEmitter = new EventEmitter();
//給“去逛街”創(chuàng)建一個(gè)監(jiān)聽(tīng)
myEmitter.on('去逛街', () => {
    console.log(`買(mǎi)了${++count}件衣服`);
});
// 再次給“去逛街”創(chuàng)建一個(gè)監(jiān)聽(tīng),不會(huì)檢查 listener 是否已被添加,依然被放到監(jiān)聽(tīng)器數(shù)組后面。
myEmitter.on('去逛街', () => {
    console.log(`買(mǎi)了${++count}條褲子`);
});
//觸發(fā)監(jiān)聽(tīng)“去逛街”這個(gè)事件
myEmitter.emit('去逛街');
myEmitter.emit('去逛街');
/** 輸出結(jié)果:
 * 買(mǎi)了1件衣服
 * 買(mǎi)了2條褲子
 * 買(mǎi)了3件衣服
 * 買(mǎi)了4條褲子*/
  • emitter.once(eventName, listener):添加單次監(jiān)聽(tīng)器 listener 到名為 eventName 的事件。 當(dāng) eventName 事件下次觸發(fā)時(shí),監(jiān)聽(tīng)器會(huì)先被移除,然后再調(diào)用。
//引入events模塊
const EventEmitter = require('events');
let count = 0;

//創(chuàng)建一個(gè)新實(shí)例
const myEmitter = new EventEmitter();
//給“去逛街”創(chuàng)建一個(gè)監(jiān)聽(tīng)
myEmitter.once('去逛街', () => {
    console.log(`買(mǎi)了${++count}件衣服`);
});
//觸發(fā)監(jiān)聽(tīng)“去逛街”這個(gè)事件
myEmitter.emit('去逛街'); // 買(mǎi)了1件衣服

myEmitter.emit('去逛街'); // 沒(méi)有被觸發(fā)
  • emitter.prependListener(eventName, listener):添加 listener 函數(shù)到名為 eventName 的事件的監(jiān)聽(tīng)器數(shù)組的開(kāi)頭。 不會(huì)檢查 listener 是否已被添加。 多次調(diào)用并傳入相同的 eventName 和 listener 會(huì)導(dǎo)致 listener 被添加多次。
  • emitter.prependOnceListener(eventName, listener):添加單次監(jiān)聽(tīng)器 listener 到名為 eventName 的事件的監(jiān)聽(tīng)器數(shù)組的開(kāi)頭。 當(dāng) eventName 事件下次觸發(fā)時(shí),監(jiān)聽(tīng)器會(huì)先被移除,然后再調(diào)用。
//引入events模塊
const EventEmitter = require('events');
//創(chuàng)建一個(gè)新實(shí)例
const myEmitter = new EventEmitter();
//給“去逛街”創(chuàng)建一個(gè)監(jiān)聽(tīng)
myEmitter.on('去逛街', () => {
    console.log(`買(mǎi)衣服`);
});
// 添加 listener 函數(shù)到名為 eventName 的事件的監(jiān)聽(tīng)器數(shù)組的開(kāi)頭。
myEmitter.prependListener('去逛街', () => {
    console.log(`買(mǎi)褲子`);
});
// 添加單次監(jiān)聽(tīng)器 listener 到名為 eventName 的事件的監(jiān)聽(tīng)器數(shù)組的開(kāi)頭
myEmitter.prependOnceListener('去逛街', () => {
    console.log(`買(mǎi)鞋子`);
});
//觸發(fā)監(jiān)聽(tīng)“去逛街”這個(gè)事件
myEmitter.emit('去逛街'); // 輸出結(jié)果:買(mǎi)鞋子 買(mǎi)褲子 買(mǎi)衣服
myEmitter.emit('去逛街'); // 輸出結(jié)果:買(mǎi)褲子 買(mǎi)衣服

3.2 調(diào)用監(jiān)聽(tīng)器

  • emitter.emit(eventName[, ...args]):按照監(jiān)聽(tīng)器注冊(cè)的順序,同步地調(diào)用每個(gè)注冊(cè)到名為 eventName 的事件的監(jiān)聽(tīng)器,并傳入提供的參數(shù)。

3.3 移除監(jiān)聽(tīng)器

  • emitter.removeListener(eventName, listener):從名為 eventName 的事件的監(jiān)聽(tīng)器數(shù)組中移除指定的 listener。每次只會(huì)從監(jiān)聽(tīng)器數(shù)組中移除一個(gè)監(jiān)聽(tīng)器。 如果監(jiān)聽(tīng)器被多次添加到指定 eventName 的監(jiān)聽(tīng)器數(shù)組中,則必須多次調(diào)用 removeListener() 才能移除所有實(shí)例。
  • emitter.removeAllListeners([eventName]):移除全部監(jiān)聽(tīng)器或指定的 eventName 事件的監(jiān)聽(tīng)器。
  • emitter.off(eventName, listener)emitter.removeListener() 的別名。
//引入events模塊
const EventEmitter = require('events');
//創(chuàng)建一個(gè)新實(shí)例
const myEmitter = new EventEmitter();
const buyClothes = () => {
    console.log(`買(mǎi)衣服`);
    myEmitter.removeListener('去逛街', buyPants);
}
const buyPants = () => {
    console.log(`買(mǎi)褲子`);
}
myEmitter.on('去逛街', buyClothes);
myEmitter.on('去逛街', buyPants);
// buyClothes 移除了監(jiān)聽(tīng)器 buyPants,但它依然會(huì)被調(diào)用。
// 觸發(fā)時(shí)內(nèi)部的監(jiān)聽(tīng)器數(shù)組為 [buyClothes, buyPants]
myEmitter.emit('去逛街'); // 買(mǎi)衣服 買(mǎi)褲子
// buyPants 現(xiàn)已被移除。
// 內(nèi)部的監(jiān)聽(tīng)器數(shù)組為 [buyClothes]
myEmitter.emit('去逛街'); // 買(mǎi)衣服

4、事件

  • 'newListener' 事件: EventEmitter 實(shí)例在新的監(jiān)聽(tīng)器被添加到其內(nèi)部監(jiān)聽(tīng)器數(shù)組之前,會(huì)觸發(fā)自身的 'newListener' 事件。在 'newListener' 回調(diào)中注冊(cè)到相同 eventName的任何其他監(jiān)聽(tīng)器將插入到正在添加的監(jiān)聽(tīng)器之前。
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
// EventEmitter 實(shí)例會(huì)在一個(gè)監(jiān)聽(tīng)器被添加到其內(nèi)部監(jiān)聽(tīng)器數(shù)組之前觸發(fā)自身的 'newListener' 事件;
// 只處理一次,避免無(wú)限循環(huán)。
myEmitter.once('newListener', (event, listener) => {
    if (event === '去逛街') {
        console.log('逛街前的準(zhǔn)備');
        myEmitter.on('去逛街', () => {
            console.log('涂防曬霜');
        });
    }
});
myEmitter.on('去逛街', () => {
    console.log('買(mǎi)護(hù)膚品');
});
myEmitter.emit('去逛街'); // 逛街前的準(zhǔn)備 涂防曬霜 買(mǎi)護(hù)膚品
myEmitter.emit('去逛街'); // 涂防曬霜 買(mǎi)護(hù)膚品

注意:對(duì)'newListener' 事件的監(jiān)聽(tīng)要放在普通監(jiān)聽(tīng)前面。如下'newListener' 事件不起作用。

const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.on('去逛街', () => {
    console.log('買(mǎi)護(hù)膚品');
});
myEmitter.once('newListener', (event, listener) => {
    if (event === '去逛街') {
        console.log('逛街前的準(zhǔn)備');
        myEmitter.on('去逛街', () => {
            console.log('涂防曬霜');
        });
    }
});
myEmitter.emit('去逛街'); // 買(mǎi)護(hù)膚品
myEmitter.emit('去逛街'); // 買(mǎi)護(hù)膚品
  • 'removeListener' 事件:'removeListener' 事件在 listener 被移除后觸發(fā)
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
const buySkinProd = () => {
    console.log('買(mǎi)護(hù)膚品');
    myEmitter.removeListener('去逛街', buySkinProd);
}
myEmitter.once('removeListener', (event, listener) => {
    if (event === '去逛街') {
        console.log('逛街結(jié)束');
    }
});
myEmitter.on('去逛街', buySkinProd);
myEmitter.emit('去逛街'); // 買(mǎi)護(hù)膚品 逛街結(jié)束

5、自定義的類繼承 events

// blog.js
const EventEmitter=require('events');

class Base extends EventEmitter {
    constructor() {
        super();
    }
    onEvent(eventName,callback){
        super.on(eventName,callback);
    }
    emitEvent(eventName,arg){
        super.emit(eventName,arg);
    }
};

class BlogInfo extends Base {
    constructor() {
        super();
    }
    onSave() {
        super.onEvent('saveStart',function(blog){
            console.log('saveStart',blog);
        });

        super.onEvent('blogCount',function(blog){
            console.log('blogCount',blog.length);
        });

        super.onEvent('saveEnd',function(blog){
            console.log('saveEnd',blog);
        });
    }
    emitEvent(blog) {
        super.emitEvent('saveStart',blog);

        super.emitEvent('blogCount',blog);

        super.emitEvent('saveEnd',blog);
    }
}

exports.blogSave=function(newblog){
    console.log(BlogInfo.__proto__.__proto__ === EventEmitter); // true
    console.log(BlogInfo.__proto__ === Base); // true
    const blogInfo=new BlogInfo();
    blogInfo.onSave(newblog);
    blogInfo.emitEvent(newblog);
};
// index.js
const http = require('http');
const blog = require('./blog');
const serve = http.createServer((req, res) => {
    if (req.url === '/') {
        const newblog = {title: "標(biāo)題", content: "內(nèi)容"};
        blog.blogSave(newblog);
        res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
        res.write('<html><body>');
        res.write('<h2>Hello World!</h2>');
        res.end('</body></html>');
    }
});
serve.listen(8000);
console.log('listen 8000');
/** 運(yùn)行index.js結(jié)果
 * listen 8000
 * true
 * true
 * saveStart [ { title: '標(biāo)題', content: '內(nèi)容' } ]
 * saveEnd [ { title: '標(biāo)題', content: '內(nèi)容' } ]
 */

參考文章:
https://www.jb51.net/article/124799.htm
http://www.itdecent.cn/p/fd1f8c998a2c
http://www.itdecent.cn/p/152fddf0628c

?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • ??events模塊是node的核心模塊,幾乎所有常用的node模塊都繼承了events模塊,比如http、fs等...
    小小的開(kāi)發(fā)人員閱讀 519評(píng)論 0 1
  • ??在 js 中事件驅(qū)動(dòng)的代碼隨處可見(jiàn),這篇文章就介紹一下 node 中的 Events 模塊,以及理解如何實(shí)現(xiàn)。...
    涯丨角閱讀 510評(píng)論 0 1
  • 前言: 因?yàn)橐郧皩W(xué)習(xí)Node.js并沒(méi)有真正意義上的去學(xué)習(xí)它,而是粗略的學(xué)習(xí)了npm的常用命令和Node.js一些...
    Srtian閱讀 1,119評(píng)論 1 17
  • Node.js EventEmitter Node.js 所有的異步 I/O 操作在完成時(shí)都會(huì)發(fā)送一個(gè)事件到事件隊(duì)...
    FTOLsXD閱讀 383評(píng)論 1 2
  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測(cè)試 ...
    KeKeMars閱讀 6,610評(píng)論 0 6

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