The EventEmitter is the class that implements listener support. Let's create a new module, models/notes-events.mjs, containing the following:
import EventEmitter from 'events';
class NotesEmitter extends EventEmitter {
noteCreated(note) { this.emit('notecreated', note); }
noteUpdate (note) { this.emit('noteupdate', note); }
noteDestroy (data) { this.emit('notedestroy', data); }
}
export default new NotesEmitter();
This module maintains the listeners to Notes-related events for us. We've created a subclass of EventEmitter because it already knows how to manage the listeners. An instance of that object is exported as the default export.
Let's now update models/notes.mjs to use notes-events to emit events. Because we have a single module, notes.mjs, to dispatch calls to the individual Notes models, this module provides a key point at which we can intercept the operations and send events. Otherwise, we'd have to integrate the event-sending code into every Notes model.
import _events from './notes-events';
export const events = _events;
We need to import this module for use here, but also export it so that other modules can also emit Notes events. By doing this, another module that imports Notes can call notes.events.function to use the notes-events module.
This technique is called re-exporting. Sometimes, you need to export a function from module A that is actually defined in module B. Module A therefore imports the function from module B, adding it to its exports.
Then we do a little rewriting of these functions:
export async function create(key, title, body) {
const note = await model().create(key, title, body);
_events.noteCreated(note);
return note;
}
export async function update(key, title, body) {
const note = await model().update(key, title, body);
_events.noteUpdate(note);
return note;
}
export async function destroy(key) {
await model().destroy(key);
_events.noteDestroy({ key });
return key;
}
The contract for the Notes model functions is that they return a Promise, and therefore our caller will be using await to resolve the Promise. There are three steps:
- Call the corresponding function in the current model class, and await its result
- Send the corresponding message to our listeners
- Return the value, and because this is an async function, the value will be received as a Promise that fulfills the contract for these functions