Re: [whatwg/dom] once() and on() async utilities for EventTarget (Issue #1038)

In general, I like this sort of idea. However, I'd caution against it because of the nuance involved with converting something that's entirely push-based (EventTarget) to something that is pull-push based (AsyncIterable). The latter is much more complicated and that complication/nuance is generally lost in async functions with for-await loops.

We looked at making RxJS's observable implement `[Symbol.asyncIterator]` but ultimately found it might be more problematic for users than beneficial. It comes down to the fact there are really 4 basic ways (and oodles of others) people will want to deal with observables in a for-await loop, and the fact that the most intuitive one of those ways has a lot of overhead (mental and technical) that people will have a hard time understanding. Ultimately, the team decided not to go ahead with the idea, and instead I [published a library to convert observables to AsyncIterables](https://www.npmjs.com/package/rxjs-for-await). 


Consider the complexity added even in this simple case:

```ts
textInput.addEventListener('input', async (e) => {
  // this is hit IMMEDIATELY every time the input is changed
  const data = await getData(textInput.value);
  render(data);
});

// vs

for await (const e of textInput.on('input')) {
  // This is hit IMMEDIATELY the FIRST time the input is changed
  // however, if the input is changed while `getData` is doing its thing,
  // then it will not be hit again until `getData` finishes doing its thing.
  // BUT, if the button is clicked two or three times while `getData` is doing 
  // its thing, THEN you have to wait for each of the previous events to be
  // processed. 
  // ADDITIONALLY, the state of `textInput.value`, and the rest of the app,
  //  will have changed by the time this is  hit in those cases, so you may
  // end up sending something you're not expecting.
  const data = await getData(textInput.value);
  render(data);
}
```

In short, while I think this improves the superficial ergonomics of `EventTarget`, ultimately I think it makes the code and the type itself harder to reason about because the underlying type and its interactions are more complex.

A pushed based type, like an observable, in likely a better choice for this use-case IMO: https://github.com/whatwg/dom/issues/544

However, I do think that there's some merit to making other things, like `ReadableStream` and the like, into AsyncIterables, as the complexity matches pretty much 1:1. 

-- 
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/dom/issues/1038#issuecomment-1006042750
You are receiving this because you are subscribed to this thread.

Message ID: <whatwg/dom/issues/1038/1006042750@github.com>

Received on Wednesday, 5 January 2022 20:11:52 UTC