/event-target-to-async-iter

Takes an event target and turns it into an async iterable

Primary LanguageTypeScriptOtherNOASSERTION

EventTarget to Async Iterable

Takes an EventTarget and an event name and turns it into an async iterable.

Note that the async iterable will not complete until either

  • an abort controller aborts it,
  • the return function is called, or
  • a returnEvent is provided and gets dispatched on the event target.

Example 1

Waiting for web socket messages:

const socket = new WebSocket('wss://example.com/socketserver');

const iter = eventTargetToAsyncIter(socket, 'message', { 
  returnEvent: 'close'
});

for await (const message of iter) {
  console.log(message)
}

Example 2

Say you have a heavily callback-based API such as HTMLRewriter and would prefer to process it as an async iterable. You can use a custom event target and eventTargetToAsyncIter:

const target = new EventTarget();
const iter = eventTargetToAsyncIter(target, 'data');

// Helper function that consumes a readable stream
async function consume(stream) {
  const reader = stream.getReader();
  while (await reader.read().then(x => !x.done)) {}
}

const response = new HTMLRewriter()
  .on('.athing[id]', {
    element(el) {
      target.dispatchEvent(new CustomEvent('data', { 
        detail: el.getAttribute('id'),
      }));
    }
  })
  .transform(await fetch('https://news.ycombinator.com'));

// No await here
consume(response.body)
  .then(() => iter.return()) // Don't create an endless loop
  .catch(e => iter.throw(e)) // Don't swallow errors

for await (const event of iter) {
  const id = Number(event.detail);
  console.log(id);
}