我有这样的代码:
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
我应该做哪些调整,同时从 2 个生成器读取数据,并得到:
1
2
a
3
4
b
5
6
c
7
8
d
9
10
e
在评论中你说:
我从运行二进制文件的库中得到 2 个生成器作为输出。一个生成器带有输出(文本行,一次生成每一行),另一个生成器包含有关进度的信息(从 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
在等待 100 毫秒之前会写入 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;
}
¹
async
术语中的“并行”。它们实际上并不是在并行线程中运行,而是交错运行(有点像“协作多任务处理”)。 JavaScript 将其执行语义限制为给定领域内的单个活动线程(“领域”宽松地表示全局环境及其中的代码)。
这里有一种方法可以将它们作为单个异步生成器使用,方法是在每次迭代中使用
Promise.race
:
async function* race([...generators]) {
const next = generator => {
const promise = generator.next().then(
({ done, value }) => ({ done, value, generator, promise }),
);
return promise;
};
const promises = generators.reduce(
(set, gen) => set.add(next(gen)),
new Set(),
);
while (promises.size > 0) {
const { done, value, generator, promise } = await Promise.race(promises);
promises.delete(promise);
if (!done) {
promises.add(next(generator));
yield value;
}
}
}
(async () => {
for await (const value of race([foo(), bar()])) {
console.log(value);
}
})();
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';
}
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
与 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() 函数首先在另一个函数之前执行,这就是为什么数字打印在字母之前。