-
Notifications
You must be signed in to change notification settings - Fork 74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
flatMap iterates sequentially instead of in parallel #244
Comments
Because we have to use But in the I was initially confused as well, but after talking with @mattpodwysocki a while back, I came to see why it's difficult. We decided the easiest solution was to The only workaround I've come up with would involve manually invoking |
I think I found a nice solution for my use case. What I wanted was actually to introduce bound concurrency, e.g. flat map the source stream with 5 items at a time max. This can be done by using Example: const flatMapConcurrent = <T, R>(
source: AsyncIterable<T>,
mapFn: (x: T) => AsyncIterable<R>,
concurrency: number
) =>
merge(...new Array<AsyncIterable<R>>(concurrency).fill(flatMap(source, mapFn))) I think we could use this implementation to introduce a wdyt? |
…ation BREAKING CHANGE: flatMap now supports concurrency fix #244
@felixfbecker I started work on flatmap-concurrent in this branch:
rewrite branch is ready @mattpodwysocki
|
FWIW I also found this behavior unexpected, but then tried to write my own and realized why it currently works this way and the complexities involved in making it concurrent. |
@felixfbecker is that The best I've got so far is to just buffer the results to gain some concurrency e.g. from(fs.createReadStream('test.log'))
.pipe(
buffer(10),
flatMap((chunks) => Promise.all(chunks.map((chunk) => process(chunk))),
fs.createWriteStream('newLog.log')
) But of course it's not particularly efficient because the 11th chunk won't get processed until all of the first 10 have finished. |
This is great! Thanks. Had to make some changes to get it working. Here is my working code: import * as Ix from "ix/asynciterable";
import * as IxO from "ix/asynciterable/operators";
export const flatMapConcurrent =
<T, R>(transform: (item: T) => AsyncIterable<R>, concurrency: number) =>
(source: AsyncIterable<T>) =>
Ix.merge(
...(new Array<AsyncIterable<R>>(concurrency).fill(
IxO.flatMap(transform)(source),
) as [AsyncIterable<R>]),
); |
Actually it doesn't work unless you import { pipe } from "fp-ts/lib/function";
import * as Ix from "ix/asynciterable";
import * as IxO from "ix/asynciterable/operators";
export const flatMapConcurrent =
<T, R>(transform: (item: T) => AsyncIterable<R>, concurrency: number) =>
(source: AsyncIterable<T>) => {
source = pipe(source, IxO.publish());
return Ix.merge(
...(new Array(concurrency).fill(pipe(source, IxO.flatMap(transform))) as [
AsyncIterable<R>,
]),
);
}; |
BREAKING CHANGE: flatMap enumerates inner sequences in parallel fix #244
I expected
flatMap
to work like RxJSflatMap
/mergeMap
. In RxJS, when the source emits items, the map function is invoked immediately and the inner emissions flattened out. Inner submissions of different outer emissions can come in in any order as map functions are potentially executed concurrently.flatMap
in Ix however seems to work more likeconcatMap
. It will completely drain the iterable returned by the map function for the first outer iterable before moving on to the next outer iterable:This makes the operator less useful because, as you can see, it would be trivial to write that out into
for await
loops. The benefit of using an operator would be concurrency.Also, it seems like how I would expect it to work is how
merge
works, but not howmergeAll
works, since that just calls intoflatMap
. This is very confusing.IxJS version: 2.3.5
The text was updated successfully, but these errors were encountered: