我正在尝试将异步生成器的简洁语法与babel配合使用(我被困在节点8上),我想知道如何将事件发射器干净地转换为异步生成器
到目前为止,我得到的是这样的
const { EventEmitter } = require('events')
// defer fonction for resolving promises out of scope
const Defer = () => {
let resolve
let reject
let promise = new Promise((a, b) => {
resolve = a
reject = b
})
return {
promise,
reject,
resolve
}
}
// my iterator function
function readEvents(emitter, channel) {
const buffer = [Defer()]
let subId = 0
emitter.on(channel, x => {
const promise = buffer[subId]
subId++
buffer.push(Defer())
promise.resolve(x)
})
const gen = async function*() {
while (true) {
const val = await buffer[0].promise
buffer.shift()
subId--
yield val
}
}
return gen()
}
async function main () {
const emitter = new EventEmitter()
const iterator = readEvents(emitter, 'data')
// this part generates events
let i = 0
setInterval(() => {
emitter.emit('data', i++)
}, 1000)
// this part reads events
for await (let val of iterator) {
console.log(val)
}
}
main()
这是很多IMO的方法,所以我问是否有人对简化此想法有所了解
答案 0 :(得分:1)
这里是另一种看法,使用 for await
循环、自定义 Symbol.asyncIterator
和用于任何潜在事件缓冲的简单队列处理计时器滴答事件。适用于 Node 和浏览器环境(RunKit、Gist)。
async function main() {
const emitter = createEmitter();
const start = Date.now();
setInterval(() => emitter.emit(Date.now() - start), 1000);
for await (const item of emitter) {
console.log(`tick: ${item}`);
}
}
main().catch(e => console.warn(`caught on main: ${e.message}`));
function createEmitter() {
const queue = [];
let resolve;
const push = p => {
queue.push(p);
if (resolve) {
resolve();
resolve = null;
}
};
const emitError = e => push(Promise.reject(e));
return {
emit: v => push(Promise.resolve(v)),
throw: emitError,
[Symbol.asyncIterator]: () => ({
next: async () => {
while(!queue.length) {
await new Promise((...a) => [resolve] = a);
}
return { value: await queue.pop(), done: false };
},
throw: emitError
})
};
}
答案 1 :(得分:0)
假设我们使用redux-saga
(因为它的核心使用generator)和socket.io作为EventEmitter
的示例
import { call, put } from 'redux-saga/effects';
function* listen() {
yield (function* () {
let resolve;
let promise = new Promise(r => resolve = r); // The defer
socket.on('messages created', message => {
console.log('Someone created a message', message);
resolve(message); // Resolving the defer
promise = new Promise(r => resolve = r); // Recreate the defer for the next cycle
});
while (true) {
const message = yield promise; // Once the defer is resolved, message has some value
yield put({ type: 'SOCKET_MESSAGE', payload: [message] });
}
})();
}
export default function* root() {
yield call(listen);
}
以上设置应为您提供了一个生成器,该生成器被事件发射器(socket.io实例)发射的下一个事件阻止。
干杯!
答案 2 :(得分:0)
我想到了这个
async *stream<TRecord extends object=Record<string,any>>(query: SqlFrag): AsyncGenerator<TRecord> {
const sql = query.toSqlString();
let results: TRecord[] = [];
let resolve: () => void;
let promise = new Promise(r => resolve = r);
let done = false;
this.pool.query(sql)
.on('error', err => {
throw err;
})
.on('result', row => {
resolve();
results.push(row);
promise = new Promise(r => resolve = r);
})
.on('end', () => {
done = true;
})
while(!done) {
await promise;
yield* results;
results = [];
}
}
到目前为止似乎仍在工作。
即您可以像在Khanh的解决方案中那样创建一个虚拟的Promise,以便可以等待第一个结果,但是随后由于许多结果可能一次全部出现,因此将它们推入数组并重置Promise以等待结果(或一批)结果)。不管这个承诺是否在其等待之前被重写数十次都没有关系。
然后,我们可以使用yield*
一次产生所有结果,并刷新数组以进行下一批处理。