Come "multicast" un iterabile asincrono?

Aug 23 2020

Un asyncgeneratore può essere in qualche modo trasmesso o multicast, in modo che tutti i suoi iteratori ("consumatori"? Abbonati?) Ricevano tutti i valori?

Considera questo esempio:

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('é', é);
})();

Le iterazioni "consumano" un valore, quindi solo l'una o l'altra lo ottiene. Vorrei che entrambi (e quelli successivi) ottenessero ogni yieldvalore ed, se un tale generatore è possibile creare in qualche modo. (Simile a un Observable.)

Risposte

3 Bergi Aug 24 2020 at 01:35

Questo non è facilmente possibile. Sarà necessario in modo esplicito tee esso. Questo è simile alla situazione per gli iteratori sincroni , solo un po 'più complicato:

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);
}

È necessario assicurarsi che entrambi gli iteratori vengano consumati all'incirca alla stessa velocità in modo che il buffer non cresca troppo.

xianshenglu Aug 23 2020 at 12:30

Intendi questo?

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, é);
})();