jamiemccrindle/axax

flatMap which returns the current iterator whenever the parent iterator updates (rxjs switchMap)

brainkim opened this issue · 2 comments

Is your feature request related to a problem? Please describe.
flatMap as currently defined like so:

export function flatMap<TFrom, TTo>(mapper: (t: TFrom) => AsyncIterable<TTo>) {
  return async function* inner(source: AsyncIterable<TFrom>) {
    for await (const item of source) {
      for await (const nestedItem of mapper(item)) {
        yield nestedItem;
      }
    }
  };
}

It takes an async iterator, applies a function to each produced value, and then delegates production to the returned async iterator. This is useful sometimes, but I’m more interested in a version of flatMap which returns the nested iterator whenever the parent iterator updates and creates a new async iterator.

Have you guys considered this?

Describe the solution you'd like

I don’t know what the solution would be yet and I probably would implement this on my own anyways. I’m just curious what y’all would name this function and how you think it should work.

Provide links to other libraries that implement similar functionality

The closest analogue from the Observable is probably switchMap.

Additional context
N/A

Describe the feature
See above.

Check out the contributing guide
The contributing guide will help you get started.

Okay I checked it out.

Have a look at how axax operators are written
Please have a look at how the other methods are written in axax e.g. they typically are curried e.g. here's map:

/**
 * Runs a mapping function over an asynchronous iterable
 */
export function map<TFrom, TTo>(
  mapper: (t: TFrom, index: number) => Promise<TTo> | TTo
) {
  return async function* inner(source: AsyncIterable<TFrom>) {
    let index = 0;
    for await (const item of source) {
      yield await mapper(item, index++);
    }
  };
}

Okay cool I looked at that too.

Thanks! I'm not 100% sure I understand what you need. Could you give an example of how you'd use it?

I think a use-case could be a chat room component. Imagine a user navigating between different chat rooms and seeing messages from each room in the same shared component. If you wanted to implement this with async iterators you could have one async iterator for room ids as the user navigates between rooms, and a subscribe function which takes a room id and returns an async iterator of messages from each room. The tricky part of this setup is that we need to prematurely return each of the iterators returned from subscribe as the room id iterator updates. Essentially, we need something like switchMap from the Observable world.

declare const roomIds: AsyncIterator<string>;
declare function subscribe(roomId: string): AsyncIterableIterator<string>;

const messages = switchMap(roomIds, (roomId) => subscribe(roomId));

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