Skip to content

Commit 99648e6

Browse files
committed
feat: fromCallback
1 parent f707c02 commit 99648e6

File tree

3 files changed

+98
-0
lines changed

3 files changed

+98
-0
lines changed

lib/from-callback-test.ts

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { assert } from 'chai'
2+
import { fromCallback } from './from-callback'
3+
import { collect } from './collect'
4+
import EventEmitter from 'events'
5+
6+
describe('fromCallback', () => {
7+
it('buffers values', async () => {
8+
const itr = fromCallback()
9+
itr.yield(1)
10+
itr.yield(2)
11+
itr.yield(3)
12+
itr.end()
13+
const values = await collect(itr)
14+
assert.deepEqual(values, [1,2,3])
15+
})
16+
it('works with event emitters', async () => {
17+
const emitter = new EventEmitter()
18+
const itr = fromCallback()
19+
emitter.on('data', itr.yield)
20+
emitter.on('close', itr.end)
21+
emitter.emit('data', 1)
22+
emitter.emit('data', 2)
23+
emitter.emit('data', 3)
24+
emitter.emit('close')
25+
const values = await collect(itr)
26+
assert.deepEqual(values, [1,2,3])
27+
})
28+
it('ignores values after end', async () => {
29+
const itr = fromCallback()
30+
itr.yield(1)
31+
itr.yield(2)
32+
itr.yield(3)
33+
itr.end()
34+
itr.yield(5)
35+
const values = await collect(itr)
36+
assert.deepEqual(values, [1,2,3])
37+
})
38+
})

lib/from-callback.ts

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { deferGenerator } from 'inside-out-async'
2+
3+
export interface CallbackIterable<T> extends AsyncIterable<T> {
4+
yield(data: T): void
5+
end(): void
6+
}
7+
8+
/**
9+
* Returns an iterable with methods to help turn event emitters or callbacks into async iterables.
10+
11+
This leverages the [`inside-out-async`](https://www.npmjs.com/package/inside-out-async#deferGenerator) package which can be used directly if you want something similar for generators. (It is bundled so it's not a dependency.)
12+
13+
It adds two methods to the returned iterable.
14+
15+
- `itr.yield(data: T): void` queues data to be read
16+
- `itr.end(): void` ends the iterable
17+
18+
And will buffer *all* data given to `yield()` until it's read.
19+
20+
```ts
21+
import { fromCallback } from 'streaming-iterables'
22+
23+
const pokeLog = fromCallback()
24+
itr.yield('Charmander')
25+
itr.yield('Ash')
26+
itr.yield('Pokeball')
27+
itr.end()
28+
29+
for await (const pokeData of pokeLog) {
30+
console.log(pokeData) // Charmander, Ash, Pokeball
31+
}
32+
33+
// To use it as a callback
34+
const emitter = new EventEmitter()
35+
const consoles = fromCallback()
36+
emitter.on('data', consoles.yield)
37+
emitter.on('close', consoles.end)
38+
39+
emitter.emit('data', 'nintendo')
40+
emitter.emit('data', 'sony')
41+
emitter.emit('data', 'sega')
42+
emitter.emit('close')
43+
44+
for await (const console of consoles) {
45+
console.log(console) // 'nintendo', 'sony', 'sega'
46+
}
47+
48+
```
49+
*/
50+
export function fromCallback<T>(): CallbackIterable<T> {
51+
const { generator, queueValue, queueReturn } = deferGenerator<T, T, undefined>()
52+
53+
const cbIterable: CallbackIterable<T> = {
54+
...generator,
55+
yield: queueValue,
56+
end: () => queueReturn()
57+
}
58+
return cbIterable
59+
}

lib/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ export { throttle } from './throttle'
2727
export { time, TimeConfig, CurriedTimeResult } from './time'
2828
export { transform } from './transform'
2929
export { writeToStream, WritableStreamish } from './write-to-stream'
30+
export { fromCallback } from './from-callback'

0 commit comments

Comments
 (0)