Javascript是一門動態語言,實現高級特性的方式有很多。發布訂閱模式是其中之一,它是建立在事件相關技術的基礎上,用于更好地組織代碼邏輯和實現松散的耦合。當模塊間需要互相通信,但又不能緊耦合的時候,發布訂閱模式發揮出很好的作用。
發布訂閱模式的核心機制就是EventEmitter類。在Node.js和瀏覽器環境中事件相關的API也都是基于該類的實現。以下是一個簡單的示例:
class EventEmitter { constructor() { this.events = {} } on(eventName, callback) { if (!this.events[eventName]) { this.events[eventName] = [] } this.events[eventName].push(callback) } emit(eventName, ...args) { const callbacks = this.events[eventName] if (callbacks) { callbacks.forEach(cb =>cb(...args)) } } removeListener(eventName, callback) { const callbacks = this.events[eventName] if (callbacks) { const index = callbacks.indexOf(callback) if (index !== -1) { callbacks.splice(index, 1) } } } }
在此基礎上,廣播(event.emit)和訂閱(event.on)行為就可以通過構造EventEmitter實例來實現了:
const event = new EventEmitter() // 添加一個監聽器 event.on('say_hello', () =>{ console.log('Hello World!') }) // 廣播事件 event.emit('say_hello') // 輸出 'Hello World!'
到了這個地步,可以加上異步廣播(event.emitAsync)和異步訂閱(event.onAsync)以及匿名監聽器支持,完善發布訂閱模式的機制:
class EventEmitter { constructor() { this.events = {} } on(eventName, callback) { if (!this.events[eventName]) { this.events[eventName] = [] } this.events[eventName].push(callback) } onAsync(eventName, asyncCallback) { this.on(eventName, function callbackWrapper(...args) { Promise.resolve(asyncCallback(...args)) .catch(console.error) }) } emit(eventName, ...args) { const callbacks = this.events[eventName] if (callbacks) { callbacks.forEach(cb =>cb(...args)) } } async emitAsync(eventName, ...args) { const callbacks = this.events[eventName] if (callbacks) { await Promise.all(callbacks.map(cb =>cb(...args))) } } removeListener(eventName, callback) { const callbacks = this.events[eventName] if (callbacks) { const index = callbacks.indexOf(callback) if (index !== -1) { callbacks.splice(index, 1) } } } }
現在,你甚至可以像這樣來注冊操作事件:
const event = new EventEmitter() // 添加異步監聽器 event.onAsync('async_event', async () =>{ await new Promise(resolve =>setTimeout(resolve, 2000)) console.log('async_event done!') }) // 添加匿名監聽器 event.onAsync('anonymous_event', async () =>{ console.log('anonymous_event done!') })
而事件的廣播和處理則仍舊像這樣:
// 廣播事件 event.emit('async_event').catch(console.error) event.emitAsync('anonymous_event').catch(console.error)
這樣,發布訂閱模式的基本機制就被實現了。你可以繼續封裝EventEmitter,使其更加符合自己的特性,比如支持一次注冊多個事件處理器、支持指定監聽器的優先級、支持在異步廣播中安全的處理錯誤等等。
總之,發布訂閱模式是一個非常有用的技術,它是Javascript中實現復雜流程控制、解耦代碼邏輯的常用方式。
下一篇httpd加php