Comment «multidiffuser» un itérable asynchrone?

Aug 23 2020

Un asyncgénérateur peut-il être en quelque sorte diffusé ou multicast, de sorte que tous ses itérateurs («consommateurs»? Abonnés?) Reçoivent toutes les valeurs?

Prenons cet exemple:

const fetchMock = () => "Example. Imagine real fetch";
async function* gen() {
  for (let i = 1; i <= 6; i++) {
    const res = await fetchMock();
    yield res.slice(0, 2) + i;
  }
}
const ait = gen();

(async() => {
  // first "consumer"
  for await (const e of ait) console.log('e', e);
})();
(async() => {
  // second...
  for await (const é of ait) console.log('é', é);
})();

Les itérations "consomment" une valeur, donc seule l'une ou l'autre l'obtient. Je voudrais que les deux (et les plus récents) obtiennent chaque yieldvaleur ed, si un tel générateur est possible de créer d'une manière ou d'une autre. (Similaire à un Observable.)

Réponses

3 Bergi Aug 24 2020 at 01:35

Ce n'est pas facilement possible. Vous devrez le mettre explicitement en tee . Ceci est similaire à la situation pour les itérateurs synchrones , juste un peu plus compliqué:

const AsyncIteratorProto = Object.getPrototypeOf(Object.getPrototypeOf(async function*(){}.prototype));
function teeAsync(iterable) {
    const iterator = iterable[Symbol.asyncIterator]();
    const buffers = [[], []];
    function makeIterator(buffer, i) {
        return Object.assign(Object.create(AsyncIteratorProto), {
            next() {
                if (!buffer) return Promise.resolve({done: true, value: undefined});
                if (buffer.length) return buffer.shift();
                const res = iterator.next();
                if (buffers[i^1]) buffers[i^1].push(res);
                return res;
            },
            async return() {
                if (buffer) {
                    buffer = buffers[i] = null;
                    if (!buffers[i^1]) await iterator.return();
                }
                return {done: true, value: undefined};
            },
        });
    }
    return buffers.map(makeIterator);
}

Vous devez vous assurer que les deux itérateurs sont consommés à peu près au même rythme afin que la mémoire tampon ne devienne pas trop grande.

xianshenglu Aug 23 2020 at 12:30

Tu veux dire ça?

async function* gen() {
  for (let i = 1; i <= 6; i++) yield i;
}
//const ait = gen();


(async() => {
  // first iteration
  const ait = gen()
  for await (const e of ait) console.log(1, e);
})();
(async() => {
  // second...
  const ait = gen()
  for await (const é of ait) console.log(2, é);
})();