there's this type of function that modern JavaScript supports that i don't see used very often, and it's called an async generator function.
i think you could use it to replace event listeners completely, since the function can just yield whenever there is a new event and the caller can await it, and both run in an infinite loop that ping-pongs back and forth.