Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read from multiple Async JavaScript generators at the same time

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
like image 871
noisy Avatar asked Jul 20 '26 20:07

noisy


1 Answers

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

like image 119
T.J. Crowder Avatar answered Jul 22 '26 09:07

T.J. Crowder



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!