如何交错/合并异步迭代? [英] How can I Interleave / merge async iterables?

查看:143
本文介绍了如何交错/合并异步迭代?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一些像这样的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);

这篇关于如何交错/合并异步迭代?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆