我正在尝试在Node.js中为可能包含数万个项目的数组构建高效的异步处理管道。管道始于对Web API的调用(使用node-fetch包),经过多个解析/转换步骤,最后以附加到磁盘上的文件结束。
但是,将某些要求放在一起,将使这变得困难:
Web API每分钟允许的请求数量有限,因此我必须能够限制/设置每次初始fetch
调用之间的延迟。这意味着该阶段必须是异步顺序的。
所有结果都写入同一文件,并且必须按照初始数组指定的顺序添加到附加文件中,因此此阶段也必须是连续的。
否则,为了提高性能,应该尽可能并行运行。例子:
a。应当能够处理较早的项目,包括文件写入步骤(假设满足要求2),同时尚未提取较晚的项目(由于第1点的限制)。
b。在最后的文件写入步骤(为了满足要求2),仅应将较晚的项目延迟较早的项目(例如,如果一个API响应主体特别大或解析时间特别长)。中间步骤的项目之间应该没有顺序依赖性。
应该注意,我正在使用节点10,因此我确实有异步迭代器/ for await
。我最接近的尝试看起来像这样(假设它在异步函数上下文中):
const valuePromises = [];
const delaySequence = itemArray.reduce(async (sequence, item) => {
await sequence;
const valuePromise = fetch(item.url)
.then(step1)
.then(step2)
.then(step3);
valuePromises.push(valuePromise);
return sleep(1000); // promisified setTimeout
}, Promise.resolve());
// If I don't do this the valuePromises array won't be fully populated:
await delaySequence;
for await (const value of valuePromises) {
await appendToFile(value);
}
除了违反上面的“ a”点之外,这是可行的,因为它必须等到所有获取操作都被触发后才能开始追加到文件中。
我曾尝试使用异步生成器,但无法提出更好的选择。
我已经考虑过使用流,它似乎适合这种任务。他们将解决顺序问题(先进先出)并允许一定程度的并行性。但是,它们的局限性在于,某个项目不能在更早的项目之前通过管道的中间阶段,这违反了“ b”。我也不知道让流与基于Promise的API交互有多么容易。
有人知道如何实现吗?
答案 0 :(得分:1)
我认为这可以实现您想要的...为“ appendToFile”序列提供单独的Promise“链”
let writeSequence = Promise.resolve();
const delaySequence = itemArray.reduce(async (sequence, item) => {
await sequence;
const valuePromise = fetch(item.url)
.then(step1)
.then(step2)
.then(step3);
writeSequence = writeSequence.then(() => valuePromise.then(appendToFile));
return sleep(1000); // promisified setTimeout
}, Promise.resolve());
很抱歉,其中有一个流浪无效的await
-现在消失了
答案 1 :(得分:1)
我遇到了同样的问题,因此决定创建自己的框架,使这种转换变得简单。它叫做scramjet
,可以满足您的需求。
您的代码看起来像这样:
DataStream.from(itemArray).
.setOptions({maxParallel: 8}) // so many executions will run in parallel
.map(item => fetch(item.url)) // promises are automatically resolved
.map(step1) // map works like on array
.map(step2)
.map(step3)
.map(async x => (await sleep(1000), x))
.stringify(someSerializer) // I guess you would stringify your data here
.pipe(fs.createWriteStream(path, {flags: "a+"}))
;
这是您的整个程序。
Scramjet会在可能的情况下生成承诺链,但会在转换流接口中公开它-因此您可以将其直接管道传输到某个位置的文件,甚至直接传输到S3。
希望有帮助。 :)