代码之家  ›  专栏  ›  技术社区  ›  Steven Lu

如何将Promise.reach或Promise.all与异步可迭代对象一起使用?

  •  0
  • Steven Lu  · 技术社区  · 4 年前

    代码示例应该比单词解释得更好:

    const spawn = require('child_process').spawn;
    
    const start = new Date();
    (async()=>{
            const proc = spawn('( echo a; >&2 echo b; sleep 1; echo c; >&2 echo d )', { shell:true });
            proc.stdout.setEncoding('utf8');
            proc.stderr.setEncoding('utf8');
            for await (const data of proc.stdout) {
                    console.log(new Date() - start, "proc stdout:", data);
            }
            for await (const data of proc.stderr) {
                    console.log(new Date() - start, "proc stderr:", data);
            }
    })();
    

    这里的输出在最后显示stderr,这对许多用例来说都很好,但我非常好奇如何从两个流中获得无延迟的输出。例如,观察到的行为是:

    5 'proc stdout:' 'a\n'
    1006 'proc stdout:' 'c\n'
    1009 'proc stderr:' 'b\nd\n'
    

    这是有道理的,因为异步流执行在stdout完全消耗之前不会到达第二个for循环。

    我想象Promise.all或race可以用来构建一种实现我想要的东西的方法,但它并没有在我面前实现。此外,for wait循环是干净访问异步可迭代对象的唯一方法吗?

    1 回复  |  直到 4 年前
        1
  •  2
  •   CertainPerformance    4 年前

    把每一个 for await 转换为异步IIFE,这样你就可以从每个IIFE中得到一个Promise。然后你可以打电话 Promise.all Promise.race 在他们身上:

    proc.stdout.setEncoding('utf8');
    proc.stderr.setEncoding('utf8');
    const stdoutProm = (async () => {
      for await (const data of proc.stdout) {
        console.log(new Date() - start, "proc stdout:", data);
      }
    })();
    const stderrProm = (async () => {
      for await (const data of proc.stderr) {
        console.log(new Date() - start, "proc stderr:", data);
      }
    })();
    await Promise.race([stdoutProm, stderrProm]);
    // One of the iterators has been completely consumed
    
        2
  •  0
  •   Jonas Wilms    4 年前

    你也可以将这两个迭代器合并为一个:

     async function* combine(a, b) {
       a = a[Symbol.iterator](); b = b[Symbol.iterator]();
       let doneA = false, doneB = false;
       let itA = a.next().then(a => ({ a })),
              itB = b.next().then(b => ({ b }));
       while(true) {
          const result = await Promise.race([itA, itB]);
          doneA = doneA || result.a && result.a.done;
          doneB = doneB || result.b && result.b.done;
          if(doneA && doneB) return;
          yield [result.a && result.a.value, result.b && result.b.value];
    
          if(result.a) itA = a.next().then(a => ({ a }))
          else itB = b.next().then(b => ({ b }));
       }
    }
    

    可用作:

     for await(const [out, err] of combine(std.out, std.err)) {
       if(out) console.log(out);
       if(err) console.log(err);
     }