WICG / observable

Observable API proposal
https://wicg.github.io/observable/
Other
575 stars 14 forks source link

Observables can be made async iterable by using a buffer #33

Open bakkot opened 1 year ago

bakkot commented 1 year ago

RxJS observables are async iterable (implemented using an unbounded buffer).

There's some good discussion of considerations and tradeoffs linked from that thread. Something to consider here.

// click-and-drag handler
element.addEventListener('mousedown', async () => {
  for await (let { clientX, clientY } of element.on('mousemove').takeUntil(element.on('mouseup')) {
    console.log('moved mouse to', clientX, clientY);
    if (clientX > threshold) {
      break; // triggers `return` handler from the async iterator, which automatically unsubscribes
    }
  }
  console.log('mouse up or exceeded threshold');
});

is pretty cute.

Jamesernator commented 1 year ago

I have found this useful in the past, however I do think it would probably be worth forcing users to pick a queueing strategy rather than just being async iterable by default. Like depending on what's done with the events I've found all of these queueing strategies useful:

benlesh commented 1 year ago

FWIW, flatMap (which is concatMap in RxJS terms) uses the exact same strategy as conversion to an async iterable does. They really aren't any different.

source.flatMap(async (v) => {
  await getSomething(v);
})
.subscribe({
  next: sideEffect
})

// is functionally identical to

for await (const v of source) {
  const result = await getSomething(v);
  sideEffect(result);
}

The major differences being that you're not allocating a Promise<IteratorResult<T>> per turn, and cancellation in the async iterable's case relies on being able to hit a break; line in the for await loop.

benlesh commented 1 year ago

FWIW: I'm 100% in favor of interop between different types. It's very useful.