如何交错/合并异步迭代? [英] How can I Interleave / merge async iterables?
问题描述
假设我有一些像这样的asnyc可迭代对象:
Suppose I have some asnyc iterable objects like this:
// Promisified sleep function
const sleep = ms => new Promise((resolve, reject) => {
setTimeout(() => resolve(ms), ms);
});
const a = {
[Symbol.asyncIterator]: async function * () {
yield 'a';
await sleep(1000);
yield 'b';
await sleep(2000);
yield 'c';
},
};
const b = {
[Symbol.asyncIterator]: async function * () {
await sleep(6000);
yield 'i';
yield 'j';
await sleep(2000);
yield 'k';
},
};
const c = {
[Symbol.asyncIterator]: async function * () {
yield 'x';
await sleep(2000);
yield 'y';
await sleep(8000);
yield 'z';
await sleep(10000);
throw new Error('You have gone too far! ');
},
};
现在,假设我可以像这样连接它们:
Now, suppose I can concat them like this:
const abcs = async function * () {
yield * a;
yield * b;
yield * c;
};
产生的(前9个)项目将是:
The (first 9) items yielded will be:
(async () => {
const limit = 9;
let i = 0;
const xs = [];
for await (const x of abcs()) {
xs.push(x);
i++;
if (i === limit) {
break;
}
}
console.log(xs);
})().catch(error => console.error(error));
// [ 'a', 'b', 'c', 'i', 'j', 'k', 'x', 'y', 'z' ]
但想象一下,我不关心订单, a
, b
和 c
以不同的速度屈服,我想尽快收益。
But imagine that I do not care about the order, that a
, b
and c
yield at different speeds, and that I want to yield as quickly as possible.
如何重写此循环以便 x
s是否尽快屈服,无视秩序?
How can I rewrite this loop so that x
s are yielded as soon as possible, ignoring order?
也可能 a
, b
或 c
是无限序列,因此解决方案不能要求将所有元素缓冲到数组中。
It is also possible that a
, b
or c
are infinite sequences, so the solution must not require all elements to be buffered into an array.
推荐答案
没有办法用循环语句写这个。 async
/ await
代码总是按顺序执行,以便同时执行需要直接使用promise combinators的事情。对于普通的承诺,有 Promise.all
,对于异步迭代器,什么都没有(还),所以我们需要自己编写它:
There is no way to write this with a loop statement. async
/await
code always executes sequentially, to do things concurrently you need to use promise combinators directly. For plain promises, there's Promise.all
, for async iterators there is nothing (yet) so we need to write it on our own:
async function* combine(iterable) {
const asyncIterators = Array.from(iterable, o => o[Symbol.asyncIterator]());
const results = [];
let count = asyncIterators.length;
const never = new Promise(() => {});
function getNext(asyncIterator, index) {
return asyncIterator.next().then(result => ({
index,
result,
}));
}
const nextPromises = asyncIterators.map(getNext);
while (count) {
const {index, result} = await Promise.race(nextPromises);
if (result.done) {
nextPromises[index] = never;
results[index] = result.value;
count--;
} else {
nextPromises[index] = getNext(asyncIterators[index], index);
yield result.value;
}
}
return results;
}
请注意组合
不支持将值传递到 next
或通过 .throw
或 .return $取消c $ c>。
Notice that combine
does not support passing values into next
or cancellation through .throw
or .return
.
您可以将其称为
(async () => {
for await (const x of combine([a, b, c])) {
console.log(x);
}
})().catch(console.error);
这篇关于如何交错/合并异步迭代?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!