Javascript Node.js:使用顺序+的并行处理;异步输入/输出

Javascript Node.js:使用顺序+的并行处理;异步输入/输出,javascript,node.js,asynchronous,promise,stream,Javascript,Node.js,Asynchronous,Promise,Stream,我正在尝试在Node.js中为可能包含数万项的数组构建一个高效的异步处理管道。管道从调用web API开始(使用node fetch包),经过多个解析/转换步骤,然后以附加到磁盘上的文件结束 然而,事实证明,如果将一些要求放在一起,则很难做到这一点: web API每分钟允许的请求数量有限,因此我必须能够限制/设置每次初始fetch调用之间的延迟。这意味着这个阶段必须是异步顺序的 所有结果都写入同一个文件,并且必须按照初始数组指定的相同顺序追加,因此此阶段也必须是连续的 否则,对于一般性能,应该

我正在尝试在Node.js中为可能包含数万项的数组构建一个高效的异步处理管道。管道从调用web API开始(使用node fetch包),经过多个解析/转换步骤,然后以附加到磁盘上的文件结束

然而,事实证明,如果将一些要求放在一起,则很难做到这一点:

  • web API每分钟允许的请求数量有限,因此我必须能够限制/设置每次初始
    fetch
    调用之间的延迟。这意味着这个阶段必须是异步顺序的

  • 所有结果都写入同一个文件,并且必须按照初始数组指定的相同顺序追加,因此此阶段也必须是连续的

  • 否则,对于一般性能,应该尽可能多地并行运行。示例:

    a。早期的项目应该能够被处理,包括文件写入步骤(假设满足要求2),同时后期的项目还没有被提取(由于第1点中的限制)

    b。在最后的文件编写步骤中(为了满足要求2),后面的项目应该只被前面的项目延迟(例如,如果一个项目有一个特别大的API响应体或特别长的解析时间)。对于中间步骤,项目之间不应有顺序依赖关系

  • 应该注意的是,我使用的是节点10,因此我确实为wait提供了异步迭代器。我最近的尝试如下所示(假设它位于异步函数上下文中):

    这是一个例外,它违反了上面的“a”点,因为它必须等到所有的回迁都被激发之后才能开始附加到文件中

    我尝试过使用异步生成器,但没有找到更好的

    我考虑过使用流,它似乎适合这种任务;它们将解决顺序问题(先进先出),并允许一定程度的并行性。但是,它们有一个限制,即一个项目不能在前一个项目之前通过管道中的中间阶段,违反了“b”。我也不知道让流与基于承诺的API接口有多容易


    有人知道如何做到这一点吗?

    我想这会满足你的要求。。。“appendToFile”序列的单独承诺“链”


    很抱歉,这里有一个无效的
    wait
    ,现在就消失了

    我也遇到了同样的问题,我决定创建自己的框架,使这些转换变得简单。它被称为,并正是你需要的

    您的代码看起来有点像这样:

    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+"}))
    ;
    
    这就是你的全部计划

    超音速燃烧冲压发动机将尽可能地生成一系列承诺,但在转换流接口中公开这些承诺——因此您可以将其直接传输到某个文件,甚至直接传输到S3


    希望有帮助。:)

    所以这避免了我在streams中提到的具体警告?另外,我对
    sleep
    调用的位置有点困惑-我唯一想要延迟的地方是连续
    fetch
    调用之间的延迟,我不确定这里的语义。欢迎提供任何其他解释(但并非预期)。除了这些问题,它看起来真的很漂亮很干净。同意-我确实把
    sleep
    调用推到了下游。我假设每分钟的请求有一定的限制,实际上不必将每个请求分隔一秒钟。上面的代码将在开始时并行执行大约9次回迁,然后流背压将解决问题。如果希望该值正好为1,请将
    sleep
    置于
    fetch
    之前,并将maxParallel设置为1。这将使变换速度减慢一点。。。如果你想合作,请通过Github与我联系,这需要一个速率限制模块,但我没有时间完成。哦,还有一个警告:是的,它只是合并转换,同时在流的末尾保持顺序。我可以确认这符合我的要求(尽管我个人会写一些不同的链).我说你建议的编辑-不确定你为什么要使用Promise.all here
    Promise.all
    只是在等待多个承诺解决时更符合我的心理模型,这只是一个偏好(建议的编辑主要是因为你修复了错误的
    Wait
    )。您仍然可以避免嵌套并将其简化一点:
    writeSequence.then(()=>valuePromise)。then(appendToFile)
    是的,如果没有等待,这正是您要做的:p
    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());
    
    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+"}))
    ;