假设我有一些像这样的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 xs 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 。
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);更多推荐
如何交错/合并异步迭代?
发布评论