91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Node.js中使用EventEmitter處理事件的案例

發布時間:2020-11-04 10:02:37 來源:億速云 閱讀:163 作者:小新 欄目:web開發

這篇文章將為大家詳細講解有關Node.js中使用EventEmitter處理事件的案例,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

在本教程中我們學習 Node.js 的原生 EvenEmitter 類。學完后你將了解事件、怎樣使用  EvenEmitter 以及如何在程序中利用事件。另外還會學習 EventEmitter 類從其他本地模塊擴展的內容,并通過一些例子了解背后的原理。

總之本文涵蓋了關于 EventEmitter 類的所有內容。

什么是事件?

當今事件驅動的體系結構非常普遍,事件驅動的程序可以產生、檢測和響應各種事件。

Node.js 的核心部分是事件驅動的,有許多諸如文件系統(fs)和  stream 這樣的模塊本身都是用 EventEmitter 編寫的。

在事件驅動的編程中,事件(event) 是一個或多個動作的結果,這可能是用戶的操作或者傳感器的定時輸出等。

我們可以把事件驅動程序看作是發布-訂閱模型,其中發布者觸發事件,訂閱者偵聽事件并采取相應的措施。

例如,假設有一個服務器,用戶可以向其上傳圖片。在事件驅動的編程中,諸如上傳圖片之類的動作將會發出一個事件,為了利用它,該事件還會有 1 到 n 個訂閱者。

在觸發上傳事件后,訂閱者可以通過向網站的管理員發電子郵件,讓他們知道用戶已上傳照片并對此做出反應;另一個訂閱者可能會收集有關操作的信息,并將其保存在數據庫中。

這些事件通常是彼此獨立的,盡管它們也可能是相互依賴的。

什么是EventEmitter?

EventEmitter 類是 Node.js 的內置類,位于 events 模塊。根據文檔中的描述:

大部分的 Node.js 核心 API 都是基于慣用的異步事件驅動的體系結構所實現的,在該體系結構中,某些類型的對象(稱為“發射器”)發出已命名事件,這些事件會導致調用 Function 對象(“監聽器”)”

這個類在某種程度上可以描述為發布-訂閱模型的輔助工具的實現,因為它可以用簡單的方法幫助事件發送器(發布者)發布事件(消息)給 監聽器(訂閱者)。

創建 EventEmitters

話雖如此,但還是先創建一個 EventEmitter 更加實在。可以通過創建類本身的實例或通過自定義類實現,然后再創建該類的實例來完成。

創建 EventEmitter 對象

先從一個簡單的例子開始:創建一個 EventEmitter,它每秒發出一個含有程序運行時間信息的事件。

首先從 events 模塊中導入 EventEmitter 類:

const { EventEmitter } = require('events');

然后創建一個 EventEmitter

const timerEventEmitter = new EventEmitter();

用這個對象發布事件非常容易:

timerEventEmitter.emit("update");

前面已經指定了事件名,并把它發布為事件。但是程序沒有任何反應,因為還沒有偵聽器對這個事件做出反應。

先讓這個事件每秒重復一次。用 setInterval()  方法創建一個計時器,每秒發布一次 update 事件:

let currentTime = 0;

// 每秒觸發一次 update 事件
setInterval(() => {
    currentTime++;
    timerEventEmitter.emit('update', currentTime);
}, 1000);

EventEmitter 實例用來接受事件名稱和參數。把 update 作為事件名, currentTime 作為自程序啟動以來的時間進行傳遞。

通過 emit() 方法觸發發射器,該方法用我們提供的信息推送事件。準備好事件發射器之后,為其訂閱事件監聽器:

timerEventEmitter.on('update', (time) => {
    console.log('從發布者收到的消息:');
    console.log(`程序已經運行了 ${time} 秒`);
});

通過 on() 方法創建偵聽器,并傳遞事件名稱來指定希望將偵聽器附加到哪個事件上。 在 update 事件上,運行一個記錄時間的方法。

on() 函數的第二個參數是一個回調,可以接受事件發出的附加數據。

運行代碼將會輸出:

從發布者收到的消息:
程序已經運行了 1 秒
從發布者收到的消息:
程序已經運行了 2 秒
從發布者收到的消息:
程序已經運行了 3 秒
...

如果只在事件首次觸發時才需要執行某些操作,也可以用 once() 方法進行訂閱:

timerEventEmitter.once('update', (time) => {
    console.log('從發布者收到的消息:');
    console.log(`程序已經運行了 ${time} 秒`);
});

運行這段代碼會輸出:

從發布者收到的消息:
程序已經運行了 1 秒

EventEmitter 與多個監聽器

下面創建另一種事件發送器。這是一個計時程序,有三個偵聽器。第一個監聽器每秒更新一次時間,第二個監聽器在計時即將結束時觸發,最后一個在計時結束時觸發:

  • update:每秒觸發一次
  • end:在倒數計時結束時觸發
  • end-soon:在計時結束前 2 秒觸發

先寫一個創建這個事件發射器的函數:

const countDown = (countdownTime) => {
    const eventEmitter = new EventEmitter();

    let currentTime = 0;

    // 每秒觸發一次 update 事件
    const timer = setInterval(() => {
        currentTime++;
        eventEmitter.emit('update', currentTime);

        // 檢查計時是否已經結束
        if (currentTime === countdownTime) {
            clearInterval(timer);
            eventEmitter.emit('end');
        }

        // 檢查計時是否會在 2 秒后結束
        if (currentTime === countdownTime - 2) {
            eventEmitter.emit('end-soon');
        }
    }, 1000);
    return eventEmitter;
};

這個函數啟動了一個每秒鐘發出一次 update 事件的事件。

第一個 if 用來檢查計時是否已經結束并停止基于間隔的事件。如果已結束將會發布 end 事件。

如果計時沒有結束,那么就檢查計時是不是離結束還有 2 秒,如果是則發布 end-soon 事件。

向該事件發射器添加一些訂閱者:

const myCountDown = countDown(5);

myCountDown.on('update', (t) => {
    console.log(`程序已經運行了 ${t} 秒`);
});

myCountDown.on('end', () => {
    console.log('計時結束');
});

myCountDown.on('end-soon', () => {
    console.log('計時將在2秒后結束');
});

這段代碼將會輸出:

程序已經運行了 1 秒
程序已經運行了 2 秒
程序已經運行了 3 秒
計時將在2秒后結束
程序已經運行了 4 秒
程序已經運行了 5 秒
計時結束

擴展 EventEmitter

接下來通過擴展 EventEmitter 類來實現相同的功能。首先創建一個處理事件的 CountDown 類:

const { EventEmitter } = require('events');

class CountDown extends EventEmitter {
    constructor(countdownTime) {
        super();
        this.countdownTime = countdownTime;
        this.currentTime = 0;
    }

    startTimer() {
        const timer = setInterval(() => {
            this.currentTime++;
            this.emit('update', this.currentTime);
    
            // 檢查計時是否已經結束
            if (this.currentTime === this.countdownTime) {
                clearInterval(timer);
                this.emit('end');
            }
    
            // 檢查計時是否會在 2 秒后結束
            if (this.currentTime === this.countdownTime - 2) {
                this.emit('end-soon');
            }
        }, 1000);
    }
}

可以在類的內部直接使用 this.emit()。另外 startTimer() 函數用于控制計時開始的時間。否則它將在創建對象后立即開始計時。

創建一個 CountDown 的新對象并訂閱它:

const myCountDown = new CountDown(5);

myCountDown.on('update', (t) => {
    console.log(`計時開始了 ${t} 秒`);
});

myCountDown.on('end', () => {
    console.log('計時結束');
});

myCountDown.on('end-soon', () => {
    console.log('計時將在2秒后結束');
});

myCountDown.startTimer();

運行程序會輸出:

程序已經運行了 1 秒
程序已經運行了 2 秒
程序已經運行了 3 秒
計時將在2秒后結束
程序已經運行了 4 秒
程序已經運行了 5 秒
計時結束

on() 函數的別名是 addListener()。看一下 end-soon 事件監聽器:

myCountDown.on('end-soon', () => {
    console.log('計時將在2秒后結束');
});

也可以用 addListener() 來完成相同的操作,例如:

myCountDown.addListener('end-soon', () => {
    console.log('計時將在2秒后結束');
});

EventEmitter 的主要函數

eventNames()

此函數將以數組形式返回所有活動的偵聽器名稱:

const myCountDown = new CountDown(5);

myCountDown.on('update', (t) => {
    console.log(`程序已經運行了 ${t} 秒`);
});

myCountDown.on('end', () => {
    console.log('計時結束');
});

myCountDown.on('end-soon', () => {
    console.log('計時將在2秒后結束');
});

console.log(myCountDown.eventNames());

運行這段代碼會輸出:

[ 'update', 'end', 'end-soon' ]

如果要訂閱另一個事件,例如 myCount.on('some-event', ...),則新事件也會添加到數組中。

這個方法不會返回已發布的事件,而是返回訂閱的事件的列表。

removeListener()

這個函數可以從 EventEmitter 中刪除已訂閱的監聽器:

const { EventEmitter } = require('events');

const emitter = new EventEmitter();

const f1 = () => {
    console.log('f1 被觸發');
}

const f2 = () => {
    console.log('f2 被觸發');
}

emitter.on('some-event', f1);
emitter.on('some-event', f2);

emitter.emit('some-event');

emitter.removeListener('some-event', f1);

emitter.emit('some-event');

在第一個事件觸發后,由于 f1f2 都處于活動狀態,這兩個函數都將被執行。之后從 EventEmitter 中刪除了 f1。當再次發出事件時,將會只執行 f2

f1 被觸發
f2 被觸發
f2 被觸發

An alias for removeListener() is off(). For example, we could have written:

removeListener() 的別名是 off()。例如可以這樣寫:

emitter.off('some-event', f1);

removeAllListeners()

該函數用于從 EventEmitter 的所有事件中刪除所有偵聽器:

const { EventEmitter } = require('events');

const emitter = new EventEmitter();

const f1 = () => {
    console.log('f1 被觸發');
}

const f2 = () => {
    console.log('f2 被觸發');
}

emitter.on('some-event', f1);
emitter.on('some-event', f2);

emitter.emit('some-event');

emitter.removeAllListeners();

emitter.emit('some-event');

第一個 emit() 會同時觸發 f1f2,因為它們當時正處于活動狀態。刪除它們后,emit() 函數將發出事件,但沒有偵聽器對此作出響應:

f1 被觸發
f2 被觸發

錯誤處理

如果要在 EventEmitter 發出錯誤,必須用 error 事件名來完成。這是 Node.js 中所有 EventEmitter 對象的標準配置。這個事件必須還要有一個 Error 對象。例如可以像這樣發出錯誤事件:

myEventEmitter.emit('error', new Error('出現了一些錯誤'));

error 事件的偵聽器都應該有一個帶有一個參數的回調,用來捕獲 Error 對象并處理。如果 EventEmitter 發出了 error 事件,但是沒有訂閱者訂閱 error 事件,那么 Node.js 程序將會拋出這個 Error。這會導致 Node.js 進程停止運行并退出程序,同時在控制臺中顯示這個錯誤的跟蹤棧。

例如在 CountDown 類中,countdownTime參數的值不能小于 2,否則會無法觸發 end-soon 事件。在這種情況下應該發出一個 error 事件:

class CountDown extends EventEmitter {
    constructor(countdownTime) {
        super();

        if (countdownTimer < 2) {
            this.emit('error', new Error('countdownTimer 的值不能小于2'));
        }

        this.countdownTime = countdownTime;
        this.currentTime = 0;
    }

    // ...........
}

處理這個錯誤的方式與其他事件相同:

myCountDown.on('error', (err) => {
    console.error('發生錯誤:', err);
});

始終對 error 事件進行監聽是一種很專業的做法。

使用 EventEmitter 的原生模塊

Node.js 中許多原生模塊擴展了EventEmitter 類,因此它們本身就是事件發射器。

一個典型的例子是 Stream 類。官方文檔指出:

流可以是可讀的、可寫的,或兩者均可。所有流都是 EventEmitter 的實例。

先看一下經典的 Stream 用法:

const fs = require('fs');
const writer = fs.createWriteStream('example.txt');

for (let i = 0; i < 100; i++) {
  writer.write(`hello, #${i}!\n`);
}

writer.on('finish', () => {
  console.log('All writes are now complete.');
});

writer.end('This is the end\n');

但是,在寫操作和 writer.end() 調用之間,我們添加了一個偵聽器。 Stream 在完成后會發出一個 finished 事件。在發生錯誤時會發出 error 事件,把讀取流通過管道傳輸到寫入流時會發出 pipe 事件,從寫入流中取消管道傳輸時,會發出 unpipe 事件。

另一個類是 child_process 類及其 spawn() 方法:

const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

ls.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});

當  child_process 寫入標準輸出管道時,將會觸發  stdoutdata 事件。當輸出流遇到錯誤時,將從 stderr 管道發送 data 事件。

最后,在進程退出后,將會觸發 close 事件。

關于Node.js中使用EventEmitter處理事件的案例就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

天门市| 大丰市| 崇文区| 水城县| 恩平市| 广饶县| 长治市| 海晏县| 宾阳县| 沛县| 华坪县| 萍乡市| 灵石县| 瓮安县| 于都县| 达拉特旗| 新龙县| 九龙县| 扎赉特旗| 吐鲁番市| 常宁市| 华阴市| 东乡族自治县| 司法| 林州市| 扶风县| 定远县| 海南省| 那坡县| 泗水县| 龙游县| 登封市| 海门市| 中宁县| 万源市| 湘乡市| 河曲县| 宁南县| 阳谷县| 清涧县| 伊金霍洛旗|