Как я могу использовать Promise.race или Promise.all с итерациями asyn c? - PullRequest
0 голосов
/ 08 февраля 2020

Пример кода должен объяснить лучше, чем могут слова:

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'

Это имеет смысл, потому что выполнение асинхронного c потока не достигает секунды за l oop до тех пор, пока stdout не будет полностью использован.

Я представляю, что Promise.all или расу можно использовать для создания способа достижения того, чего я хочу, но это не материализуется передо мной. Кроме того, являются ли циклы ожидания единственным способом доступа к асинхронным итерациям c без ошибок?

Ответы [ 2 ]

2 голосов
/ 08 февраля 2020

Поместите каждый for await в асин c IIFE, чтобы вы могли получить Обещание из каждого. Тогда вы можете позвонить 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
0 голосов
/ 08 февраля 2020

Вы также можете объединить два итератора в один:

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