concurrent
Evaluates up to n elements of an async pipeline at once, while the result still arrives in source order.
Lecture
concurrent(n) is FxDart's answer to "run several async steps
in parallel, but keep the results in order." It works through the
concurrency-marker model baked into
FxAsyncIterator.next([Concurrent? concurrent]): when you call
.concurrent(3), every pull through it passes a
Concurrent(3) marker upstream, one layer at a time,
telling whatever produced the values ("evaluate 3 of you at once instead
of one"). The upstream operator — typically a lazy map — sees
that marker and, instead of awaiting one Future and then starting the
next, calls its own source's next() three times without
waiting in between, so three Futures are in flight simultaneously. As each
settles, concurrent buffers the result but only releases
values to you in the original order — so results always come out
matching the input sequence, even if a later item finishes before an
earlier one.
This is the back-channel that toAsync's lecture mentioned:
Dart's Stream has no way to ask an upstream source "give me
3 at once" after the fact, because a Stream pushes at its own
pace. FxDart's pull-based next() protocol carries that
request upstream on every single pull, which is what makes
concurrent(n) possible at all.
Tune n based on what you're limited by: a REST API might
tolerate 5-10 concurrent requests, and n = 1 is
equivalent to plain sequential awaiting (which is exactly what you get
without concurrent at all). CPU-bound work is
parallel, not a bigger
n — see
concurrent or parallel.
At N=100k of a zero-delay fetch, a hand-rolled worker pool is still
about 10% faster (AOT). That remaining tax is the ordered-batch
machinery, not the map layer — you pay it for the chain.
Demo 1 · Sequential vs. concurrent(3), timed
Six items, each with a 200ms delay. Sequential takes ~1200ms; asking for 3 at a time cuts it to ~400ms:
Demo 2 · Order is preserved, even when completion order isn't
Item 2 finishes before item 1 here (100ms vs. 300ms), but
concurrent(3) still hands back results in source order —
compare this with concurrentPool,
which does the opposite:
Try it yourself
Exercise: this pipeline processes 6 items of 200ms each, sequentially
(n = 1). Tune n up and watch the elapsed time
drop — try 3, then 6.
concurrentPool — completion-order variant ·
toAsync — the pull-based model this relies on ·
async variants — the *Async naming convention ·
map — the operator most often paired with concurrent ·
concurrent or parallel — I/O vs CPU