本页尚未翻译,因此以英文显示。 参与翻译

concurrent

Evaluates up to n elements of an async pipeline at once, while the result still arrives in source order.

FxAsyncIterable<A> concurrentAsync<A>(int length, FxAsyncIterable<A> iterable) FxAsync<T> FxAsync.concurrent(int length) // chain

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.

Related: 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