如何同时从多个异步JavaScript生成器中读取



我有一个这样的代码:

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

产生:

1
2
3
4
5
6
7
8
9
10
a
b
c
d
e

我应该做什么调整,同时从两个生成器中读取,并获得:

1
2
a
3
4
b
5
6
c
7
8
d
9
10
e

在一条评论中,您说:

我从运行二进制文件的库中得到两个生成器作为输出。一个生成器具有输出(文本行,每行一次生成(,另一个生成器包含进度信息(从0到100的数字(。当然,我希望能够在显示输出的同时显示进度。

在这种情况下,通过异步生成器并行循环:

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

但是请注意,如果您对问题中的特定两个合成生成器执行此操作,则输出不会完全符合您所说的,因为foo在等待100ms之前同步写入1,类似地,bar在等待200ms之前同步写a,因此,结果从1 a 2 b开始,然后从3 4 c 5 6 d 7 8 e继续,以此类推。它们按照您所说的进行了交错,只是那些合成的结果并没有达到您在写问题时所期望的时间。

实例:

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


;平行的";在CCD_ 7术语中。它们并不是在并行线程中运行,而是交错的(有点像"协作多任务处理"(。JavaScript将其执行语义限制为给定领域内的单个活动线程("领域"松散地表示全局环境及其代码(。

以下是一种将它们作为单个异步生成器使用的方法,通过在每次迭代中使用Promise.race

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

类似于TJ的函数方法,您可以将每个循环封装在IIFE:中

编者按:最初这个答案包括承诺,因此TJ的评论。

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

在JavaScript引擎中,尤其是使用异步函数时,我认为这是不可能的,因为解释器是按顺序读取的,当你有两个console.log((函数时,第一个console.log((函数首先执行,然后执行另一个,这就是为什么数字会打印在字母之前。

最新更新