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

时间:2020-02-07 22:08:18

标签: javascript async-await

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

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 await循环是唯一干净访问异步可迭代对象的唯一方法吗?

2 个答案:

答案 0 :(得分:2)

将每个for await放入异步IIFE中,这样您就可以从中获得一个Promise。然后,您可以呼叫Promise.allPromise.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

答案 1 :(得分:0)

您还可以将两个迭代器组合为一个:

 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);
 }