最近我开始思考 - 我是否以正确的方式处理Nodejs流中的异步操作?因此,我只是想确保它是这样。
class AsyncTransform extends Transform {
constructor() {
super({objectMode: true});
}
public async _transform(chunk, enc, done) {
const result = await someAsyncStuff();
result && this.push(result);
done();
}
}
这个小例子效果很好,但基本上,所有异步的东西都需要一些时间来执行,在大多数情况下,我想要并行处理块。我会在done
的顶部放置_transform
,但由于某些原因,这不是解决方案,其中一个原因是当最后一个块将调用done
时,下一个push
会抛出错误Error: stream.push() after EOF
。因此,如果最后一个块已经使用done
调用,我们就无法推送。
对于处理这种情况,我将_flush
转换的方法与chunk的队列计数器结合使用(当它进入时增加,在push
减少)。该死的,已经有这么多的话了,所以这里只是我代码的一个例子。
const minute = 60000;
const second = 1000;
const CRITICAL_QUEUE_POINT = 50;
export class AsyncTransform extends Transform {
private queue: number = 0;
constructor() {
super({objectMode: true});
}
public async _transform(chunk, enc, done) {
this.checkQueue()
.then(() => this.init(chunk, done));
}
public _flush(done) {
this._done(done, true);
}
private async init(chunk, done) {
this.increaseQueueCounter();
this._done(done);
const user = await new UserRepository().search(chunk);
this.decreaseQueueCounter();
this._push(user);
}
/**
* Queue
* */
private checkQueue(): Promise<any> {
return new Promise((resolve) => {
const _checkQueue = () => {
if (this.queue >= CRITICAL_QUEUE_POINT) {
return setTimeout(_checkQueue, second * 10);
}
resolve();
};
_checkQueue();
});
}
private increaseQueueCounter(): void {
this.queue++;
}
private decreaseQueueCounter(): void {
this.queue--;
}
/**
* Transform API
* */
private _push(user) {
this.push(user);
}
private _done(done, isFlush: boolean = false) {
if (!isFlush) {
return done();
}
if (this.queue === 0) {
return done();
}
setTimeout(() => this._done(done, isFlush), second * 10);
}
}
答案 0 :(得分:1)
两年前我考虑过这个问题并且正在寻找解决方案 - 某种框架。我找到了几个框架 - 高地和事件流 - 但是所有框架都使用起来非常复杂,我决定写一个新框架:scramjet。
现在您的代码可以简单:
const {DataStream} = require('scramjet');
yourDataStream.pipe(new DataStream())
.map(async (chunk) => {
await checkQueue();
return new UserRepository().search(chunk);
});
或者,如果我正确理解checkQueue(),它只会将同时连接数保持在临界水平以下,那么它就更简单了:
yourDataStream.pipe(new DataStream({maxParallel: CRITICAL_QUEUE_POINT }))
.map(async (chunk) => new UserRepository().search(chunk));
它会将连接数保持在一个稳定的水平(每当有一个响应它会开始一个新线程)。