emiterator
Version:
Adapter that turns EventEmitters into AsyncGenerators
85 lines (74 loc) • 3.24 kB
Markdown
# emiterator
Turn node's [EventEmitters](https://nodejs.org/api/events.html#class-eventemitter) into [AsyncGenerators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator). This allows you to use the `for await...of` loop instead of
writing event listener functions.
For example, to iterate through text stream:
```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.`);
}
```
###### _Note: this example is unnecessary in practice; Since node 10 a Readable can be iterated directly._
<br/>
The module exports a single [async generator function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function*) named 'emiterator'.
Both ESM and commonJs modules are included, so either `require` or `import` should work:
```javascript
import { emiterator } from 'emiterator'; // ESM
const { emiterator } = require('emiterator'); // commonJs
```
## Strong TypeScript Support
```typescript
/**
* Makes an AsyncGenerator from an EventEmitter.
* Behind the scenes, listeners are registered for each event specified.
* They are removed when any of the `doneEvents` or `throwEvents` are emitted.
*
* @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.
* @yields Union of objects with string `event` and tuple `args` properties
* that map to what would otherwise be emitter's registered listener
* functions for doneEvents. I.e. `on(event, (...args)=>{...})`
*/
export async function *emiterator(
emitter: EventEmitter,
dataEvents: string[],
doneEvents: string[],
throwEvents: string[] = []
): AsyncGenerator<{event: string, args: any[]}, void, undefined> {...}
```
###### _Note: Simplified typing shown. The typing is much more useful when using a typed emitter interface. See below._
<br/>
If `emitter` implements one of the types from
[tiny-typed-emitter](https://www.npmjs.com/package/tiny-typed-emitter) or
[typed-emitter](https://www.npmjs.com/package/typed-emitter),
then elements that are yielded each iteration will be typed accordingly:
```typescript
interface ItemCounterEventListeners {
item: (s:string) => any
count: (n:number) => any
done: () => any
}
const emitter: TypedEmitter<ItemCounterEventListeners> = getCounter();
const iterator = emiterator(
emitter,
['item', 'count'],
['done']
);
for await (let e of iterator) {
// here, e is {event: 'item', args: [string]} | {event: 'count', args: [number]}
if ('count' == event) {
// your IDE should show e.args is a [number] here...
}
}
```