emiterator
Version:
Adapter that turns EventEmitters into AsyncGenerators
85 lines • 3.39 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.emiterator = void 0;
/**
* Makes an AsyncGenerator from an EventEmitter.
* ```typescript
* import { createReadStream } from 'node:fs';
* import { emiterator } from 'emiterator';
*
* let iterator = emiterator(
* createReadStream('file.txt', 'utf-8'),
* ['data'],
* ['end']
* );
*
* for await (let chunk of iterator) {
* // chunk is { event: 'data', args: [string] }
* console.log(`Received ${chunk.args[0].length} bytes of data.`);
* }
* ```
* Behind the scenes, listeners are registered for each event specified.
* They are removed when any of the `doneEvents` or `throwEvents` are emitted.
*
* @template L Mapping of event names to associated listener signatures.
* Defaults to `{ [k: string]: (...args: any[]) => any; }`
* @template DataEvent The names of events that will be iterated over.
* @template DoneEvent The names of the events that signal iteration is done.
* E.g. 'end' | 'finish'
* @template ThrowEvent The names of events that signal an error. E.g. 'error'.
*
* @param emitter The EventEmitter. If emitter is a TypedEmitter, then the
* elements yielded by the AsyncGenerator will typed accordingly.
* @param dataEvents Events that will be yielded by the AsyncGenerator.
* @param doneEvents Events that signal the end of iteration.
* @param throwEvents Events that will cause the iterator to throw.
* To handle these events yourself, add them to `dataEvents` instead.
* @returns An AsyncGenerator that yields objects with string `event` and tuple
* `args` properties that map to what would otherwise be emitter's
* dataEvents and associated listener function arguments.
* I.e. `on(event, (...args)=>{...})`
*/
async function* emiterator(emitter, dataEvents, doneEvents, throwEvents = []) {
let results = [];
let resolve;
let reject;
let promise = new Promise((...args) => [resolve, reject] = args);
const listeners = new Map();
const addListener = (event, listener) => {
emitter.on(event, listener);
let eventListeners = listeners.get(event);
if (null == eventListeners) {
eventListeners = [];
listeners.set(event, eventListeners);
}
eventListeners.push(listener);
};
const removeListeners = () => listeners.forEach((eventListeners, event) => eventListeners.forEach(eventListener => //@ts-ignore
emitter.removeListener(event, eventListener)));
for (const dataEvent of dataEvents) {
addListener(dataEvent, (...args) => {
results.push({ event: dataEvent, args });
resolve(true);
promise = new Promise((...args) => [resolve, reject] = args);
});
}
for (const doneEvent of doneEvents) {
addListener(doneEvent, (..._args) => {
removeListeners();
resolve(false);
});
}
for (const throwEvent of throwEvents) {
addListener(throwEvent, (...errs) => {
removeListeners();
const err = null == errs?.[0] ? new Error(String(throwEvent)) : errs[0];
reject(err);
});
}
while (await promise) {
yield* results;
results = [];
}
}
exports.emiterator = emiterator;
//# sourceMappingURL=index.js.map