-
Notifications
You must be signed in to change notification settings - Fork 14
Description
Since the API is so similar to Observable, I find it great for consuming observable-like things but I'm not as sold for using it on non-observable-like things, like transducers or other async iterator combiners.
It seems almost simpler to implement transducers like map
correctly using a raw async iterator protocol than using Repeater
.
The naive assumption (in repeaterMap
below) is to use a for await
loop, but this keeps consuming input after the caller returns, which would be especially bad if the input is infinite. Whereas a raw async iterator can immediately forward return and throw calls to the input.
A less naive attempt (awkwardRepeaterMap
) is to do the for await
in a separate function and break once stop is detected. However more calls to next()
still go through than with the raw async iterator.
Thoughts?
Code
const { Repeater } = require('@repeaterjs/repeater')
const range = n => ({
i: 0,
n,
async next() {
console.log('next()')
const i = ++this.i
if (i >= this.n) return { done: true }
return { value: i, done: false }
},
async return() {
console.log('return()')
this.i = this.n
return { done: true }
},
async throw(error) {
console.log('throw()')
this.i = this.n
return { done: true }
},
[Symbol.asyncIterator]() {
return this
},
})
const repeaterMap = (iterable, iteratee) =>
new Repeater(async (push, stop) => {
for await (const value of iterable) push(iteratee(value))
await stop
})
const rawMap = (iterable, iteratee) => ({
iterator: iterable[Symbol.asyncIterator](),
async next() {
const { value, done } = await this.iterator.next()
return { value: iteratee(value), done }
},
return() {
return this.iterator.return()
},
throw(error) {
return this.iterator.throw(error)
},
[Symbol.asyncIterator]() {
return this
},
})
const awkwardRepeaterMap = (iterable, iteratee) =>
new Repeater(async (push, stop) => {
let stopped = false
async function next() {
for await (const value of iterable) {
if (stopped) break
push(iteratee(value))
}
}
next()
await stop
stopped = true
})
async function go() {
console.log('rawMap')
for await (const i of rawMap(range(10), i => i * 2)) {
console.log(i)
if (i >= 5) break
}
console.log('\nrepeaterMap')
for await (const i of repeaterMap(range(10), i => i * 2)) {
console.log(i)
if (i >= 5) break
}
console.log('\nawkwardRepeaterMap')
for await (const i of awkwardRepeaterMap(range(10), i => i * 2)) {
console.log(i)
if (i >= 5) break
}
}
go()
Output
rawMap
next()
2
next()
4
next()
6
return()
repeaterMap
next()
next()
next()
2
next()
next()
next()
4
next()
next()
6
next()
next()
awkwardRepeaterMap
next()
next()
next()
2
next()
next()
next()
4
next()
next()
6
next()
return()