I have a code like this:
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function* foo() {
yield 1;
await delay(100);
yield 2;
await delay(100);
yield 3;
await delay(100);
yield 4;
await delay(100);
yield 5;
await delay(100);
yield 6;
await delay(100);
yield 7;
await delay(100);
yield 8;
await delay(100);
yield 9;
await delay(100);
yield 10;
}
async function* bar() {
yield 'a';
await delay(200);
yield 'b';
await delay(200);
yield 'c';
await delay(200);
yield 'd';
await delay(200);
yield 'e';
}
(async function () {
for await (const num of foo()) {
console.log(num);
}
for await (const str of bar()) {
console.log(str);
}
await delay(2000);
})();
which produce:
1
2
3
4
5
6
7
8
9
10
a
b
c
d
e
What addjustments I should make, to read from 2 generators at the same time, and get:
1
2
a
3
4
b
5
6
c
7
8
d
9
10
e
In a comment you said:
I get 2 generators as output from libary which run a binary. One generator is with output (lines of texts, each line yielded at a time), and another generator contains information about progress (numbers from 0 to 100). Of course I want to be able to display progress simultaneously while displaying output.
In that case, loop through the async generators in parallel:¹
async function show(g) {
for await (const value of g) {
console.log(value);
}
}
(async function () {
// Start processing the first one
const pFoo = show(foo());
// Start processing the second one
const pBar = show(bar());
// Wait until both are done
await Promise.all([pFoo, pBar]);
})();
But, note that if you do that with the specific two synthetic generators in your question, the output won't be exactly what you've said you wanted, because foo writes 1 synchronously before waiting 100ms, and similarly bar writes a synchronously before waiting 200ms, so the results start with 1 a 2 b and then continue with 3 4 c 5 6 d 7 8 e and so on. They are interleaved as you've said you wanted, just those synthetic ones don't quite have the timing you were expecting when writing the question.
Live Example:
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function* foo() {
yield 1;
await delay(100);
yield 2;
await delay(100);
yield 3;
await delay(100);
yield 4;
await delay(100);
yield 5;
await delay(100);
yield 6;
await delay(100);
yield 7;
await delay(100);
yield 8;
await delay(100);
yield 9;
await delay(100);
yield 10;
}
async function* bar() {
yield "a";
await delay(200);
yield "b";
await delay(200);
yield "c";
await delay(200);
yield "d";
await delay(200);
yield "e";
}
async function show(g) {
for await (const value of g) {
console.log(value);
}
}
(async function () {
const pFoo = show(foo());
const pBar = show(bar());
await Promise.all([pFoo, pBar]);
})();
.as-console-wrapper {
max-height: 100% !important;
}
¹ "Parallel" in async terms. They don't literally run in parallel threads, they interleave (sort of like "cooperative multitasking"). JavaScript restricts its execution semantics to only a single active thread within a given realm ("realm" loosely means global environment and the code in it).
Similar to TJ's functional approach, you can wrap each loop in an IIFE:
edit note: Originally this answer included promises, hence TJ's comments.
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function* foo() {
yield 1;
await delay(100);
yield 2;
await delay(100);
yield 3;
await delay(100);
yield 4;
await delay(100);
yield 5;
await delay(100);
yield 6;
await delay(100);
yield 7;
await delay(100);
yield 8;
await delay(100);
yield 9;
await delay(100);
yield 10;
}
async function* bar() {
yield 'a';
await delay(200);
yield 'b';
await delay(200);
yield 'c';
await delay(200);
yield 'd';
await delay(200);
yield 'e';
}
(async function () {
const numPromise = (async () => {
for await (const num of foo()) {
console.log(num);
}
})();
const strPromise = (async () => {
for await (const str of bar()){
console.log(str);
}
})();
await Promise.all([numPromise,strPromise])
console.log('Done!')
await delay(2000);
})();
Here's a way you can consume them as a single async generator, by using Promise.race on each iteration:
async function* race(iterable) {
const generators = [...iterable];
const next = gen => {
const promise = gen.next().then(
({ done, value }) => ({ done, value, gen, promise }),
);
return promise;
};
const promises = generators.reduce(
(set, gen) => set.add(next(gen)), new Set(),
);
while (promises.size > 0) {
const { done, value, gen, promise } = await Promise.race(promises);
promises.delete(promise);
if (!done) {
promises.add(next(gen));
yield value;
}
}
}
(async () => {
for await (const value of race([foo(), bar()])) {
console.log(value);
}
})();
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function* foo() {
yield 1;
await delay(100);
yield 2;
await delay(100);
yield 3;
await delay(100);
yield 4;
await delay(100);
yield 5;
await delay(100);
yield 6;
await delay(100);
yield 7;
await delay(100);
yield 8;
await delay(100);
yield 9;
await delay(100);
yield 10;
}
async function* bar() {
yield 'a';
await delay(200);
yield 'b';
await delay(200);
yield 'c';
await delay(200);
yield 'd';
await delay(200);
yield 'e';
}