Closed otakustay closed 7 years ago
Possible example can help you https://jsfiddle.net/173hp99p/6/ It convert mouse events to generator
Currently this example works in Google Chrome Canary with flags --args --js-flags="--harmony-async-iteration"
Personally I've found when I want streaming that I can consume when I actually want it that the best way tends to be to use an async queue like this (it's not coincidence the constructor looks is nearly identical to Observable):
function deferred() {
const def = {}
def.promise = new Promise((resolve, reject) => {
def.resolve = resolve
def.reject = reject
})
return def
}
class AsyncQueue {
constructor(initializer) {
// This should probably be a linked list but eh
// implementation details
this.queue = []
this.waiting = []
initializer({
next: value => {
if (this.waiting.length > 0) {
// If anyone is waiting we'll just send them the value
// immediately
const consumer = this.waiting.shift()
consumer.resolve({
done: false,
value
})
} else {
return this.queue.push({
type: 'next',
value
})
}
},
throw: error => {
if (this.waiting.length > 0) {
const consumer = this.waiting.shift()
return consumer.reject(error)
} else {
return this.queue.push({
value: error,
type: 'error'
})
}
},
return: value => {
if (this.waiting.length > 0) {
const consumer = this.waiting.shift()
return consumer.resolve({
done: true,
value
})
} else {
return this.queue.push({
value,
type: 'return'
})
}
}
})
}
next() {
if (this.queue.length > 1) {
// If there are items available then simply put them
// into the queue
const item = this.queue.shift()
if (item.type === 'return') {
return Promise.resolve({
done: true,
value: item.value
})
} else if (item.type === 'error') {
return Promise.reject(item.value)
} else {
return Promise.resolve({
done: false,
value: item.value
})
}
} else {
// If there's nothing available then simply
// give back a Promise immediately for when a value eventually
// comes in
const def = deferred()
this.waiting.push(def)
return def.promise
}
}
[Symbol.asyncIterator]() {
return this
}
}
const ticks = new AsyncQueue(iter => {
let i = 0
setInterval(_ => {
iter.next(i)
i += 1
}, 1000)
})
Usage:
const ticks = new AsyncQueue(iter => {
let i = 0
setInterval(_ => {
iter.next(i)
i += 1
}, 1000)
})
async function main() {
for await (const i of ticks) {
console.log(i)
}
}
main()
And here's a plunk demonstrating mouse events.
I had a very similar idea, proof of concept here: https://github.com/KeithHenry/event-generator
Live demo at https://keithhenry.github.io/event-generator/, but it only works in Chrome Canary 60+ with --js-flags=--harmony-async-iteration enabled.
That gives me observable-style events:
// Create a generator that iterates each time the click event happens
const eventIterator = eg(testPanel, 'click');
for await (const e of eventIterator)
console.log('Clicked', e);
Closing since it seems people have worked out how to do this, and in any case it's not a bug with the proposal that we should continue tracking :)
This is not a question related to spec itself, but is there any practical pattern to listen on an event (
XMLHttpRequest#onprogress
for example) and convert it to a async generator (each trigger of event becomes to an yield)?If it is not possible to play with generator, how can I manually create an iterator to consume events?