ブラウザに EventEmitter はいらなかった
ブラウザでも EventEmitter が使いたい時があるので自分で作ってることがあります
完全互換は面倒ですが最低限 emit/on/off くらいならそこまで難しいものでもないです
webpack とかを使ってる場合は npm パッケージで EventEmitter2 とか EventEmitter3 とかいれることも

ですが 特に何も入れなくても EventEmitter に相当するものが使えました

const et1 = new EventTarget()
et1.addEventListener("foo", eve => {
console.log(eve.detail)
})

et1.dispatchEvent(new CustomEvent("foo", { detail: "ABCD" }))
// ABCD
const et2 = new EventTarget()
et2.addEventListener("foo", ({ detail }) => console.log(detail), { once: true })

et2.dispatchEvent(new CustomEvent("foo", { detail: "1" }))
// 1
et2.dispatchEvent(new CustomEvent("foo", { detail: "2" }))
// (出力なし)

emit 代わりが自分で Event を作らないといけないので少し面倒
それに on も addEventListener と名前が長め
EventEmitter と同じように使える on と emit メソッドを追加しようとも考えましたが リスナ関数をラップすることになって remove するためにラップ関数を管理する必要もでてきてめんどうです
なので on は名前を変えるだけで emit のみ短く書けるように簡単なものを用意しました

class EventEmitter0 extends EventTarget {
emit(type, value) {
this.dispatchEvent(new CustomEvent(type, { detail: value }))
}

on(...a) {
this.addEventListener(...a)
}
}

const ee = new EventEmitter0()
ee.on("foo", ({ detail }) => console.log(detail))
ee.emit("foo", "X")
// X
EventEmitter を Promise や Generator でする
◯ EventEmitter の基本的な使い方

毎秒時刻をコンソールに表示

const emitter = new EventEmitter()
emitter.on("aaa", value => console.log(value))
setInterval(() => emitter.emit("aaa", Date.now()), 2000)

Promise も非同期処理の通知ができるけど一回限り
二回目以降の通知はできず 終了済みの値は保持されて終了してれば待機せず即実行できる

◯ Promise で EventEmitter みたいなことをやってみる

Promise の結果に本来の結果と次の Promise を入れて 結果を受け取ったら次は新しく受け取った Promise の resolve を待機する

const notifyPromise = () => {
let resolve
const promise = new Promise(a => (resolve = a))
const notify = value => {
const r = resolve
const p = new Promise(a => (resolve = a))
r([value, p])
}
return [notify, promise]
}

const [notify, promise] = notifyPromise()
Promise.resolve().then(async () => {
let p = promise
while (true) {
const [value, next] = await p
try {
console.log(value)
} finally {
p = next
}
}
})
setInterval(() => notify(Date.now()), 2000)

await の無限ループにするので then の中でやらないとメインの処理が進まなくなる
Promise の入れ替えなどちょっと面倒

◯ for await of のループを利用する

generator にして Promise を受け取る
for-of を使って Promise の入れ替えを考えなくていいようにする

const f = () => {
let resolve
const wait = () => new Promise(r => (resolve = r))
async function* g() {
while (true) yield await wait()
}
const notify = v => resolve(v)
const it = g()
return [notify, it]
}

const [notify, it] = f()
Promise.resolve().then(async () => {
for await (const value of it) {
console.log(value)
}
})
setInterval(() => notify(Date.now()), 2000)

generator の中で作った関数を公開して その関数の中で yield できたら楽だったけど generator スコープ中でしか yield できない
なので emit に当たる関数が呼び出されるまで待機するには await での待機が必要
async generator にして await で待機してから yield する