Consuming Asynchronous Sequences
All AsyncSeq<'T> values are lazy — they only produce elements when actively consumed. This
document covers the full range of consumption patterns, from iterating with a side effect to
collecting into an array, searching for an element and computing aggregate values.
open FSharp.Control
let oneThenTwo = asyncSeq {
yield 1
do! Async.Sleep 1000
yield 2
}
Iterating with a For Loop
Inside any async { ... } computation, you can iterate an AsyncSeq with a plain for loop.
The loop body may contain let! and do! bindings just like the rest of the async block:
async {
for x in oneThenTwo do
printfn "Got %d" x
} |> Async.RunSynchronously
This is the most natural way to consume a sequence when you already have an async context,
such as an application entry point or an async test.
iter and iterAsync
AsyncSeq.iter applies a synchronous action to every element and returns Async<unit>:
let numbers = asyncSeq { yield! [ 1 .. 5 ] }
let printAll : Async<unit> =
numbers |> AsyncSeq.iter (printfn "item: %d")
AsyncSeq.iterAsync does the same but the action returns Async<unit>, which is awaited before
the next element is consumed. This makes it ideal for actions that themselves do IO — such as
writing to a database or calling an API:
let processItem (n: int) : Async<unit> =
async { printfn "processing %d" n }
let processAll : Async<unit> =
numbers |> AsyncSeq.iterAsync processItem
iteri and iteriAsync
AsyncSeq.iteri and AsyncSeq.iteriAsync are the same but also pass a zero-based integer
index to the action, useful for logging progress or tagging elements:
let printIndexed : Async<unit> =
numbers |> AsyncSeq.iteri (fun i n -> printfn "[%d] %d" i n)
iterAsyncParallel
AsyncSeq.iterAsyncParallel processes elements concurrently — the action for each element is
started as soon as the element is available, without waiting for the previous action to finish.
Use this when the actions are independent and you want maximum throughput:
let processAllParallel : Async<unit> =
numbers |> AsyncSeq.iterAsyncParallel processItem
AsyncSeq.iterAsyncParallelThrottled is the same but limits the number of concurrent actions:
let processThrottled : Async<unit> =
numbers |> AsyncSeq.iterAsyncParallelThrottled 4 processItem
Folding
AsyncSeq.fold accumulates a state over all elements using a synchronous folder function. It
is the most general consumption primitive — all other aggregations can be implemented with it:
let sum : Async<int> =
numbers |> AsyncSeq.fold (fun acc n -> acc + n) 0
AsyncSeq.foldAsync is the same but the folder returns Async<'State>, for cases where
accumulating a value requires async work:
let asyncSum : Async<int> =
numbers |> AsyncSeq.foldAsync (fun acc n -> async { return acc + n }) 0
reduceAsync
AsyncSeq.reduceAsync is a fold without an explicit seed — it uses the first element as the
initial state. It raises InvalidOperationException on an empty sequence:
let words = asyncSeq { yield! [ "F#"; "is"; "great" ] }
let sentence : Async<string> =
words |> AsyncSeq.reduceAsync (fun acc w -> async { return acc + " " + w })
Searching
pick and tryPick
AsyncSeq.pick applies a chooser function to each element and returns the first Some result,
raising KeyNotFoundException if the sequence is exhausted without a match:
let firstEven : Async<int> =
numbers |> AsyncSeq.pick (fun n -> if n % 2 = 0 then Some n else None)
AsyncSeq.tryPick is the safe variant — it returns Async<'T option> and returns None
instead of raising when there is no match:
let maybeFirstOver100 : Async<int option> =
numbers |> AsyncSeq.tryPick (fun n -> if n > 100 then Some n else None)
AsyncSeq.pickAsync and AsyncSeq.tryPickAsync accept choosers that return Async<_ option>,
for cases where the matching decision requires async IO.
exists and forall
AsyncSeq.exists returns true as soon as it finds an element satisfying the predicate, and
short-circuits consumption at that point. AsyncSeq.forall returns false as soon as it finds
an element that does not satisfy the predicate:
let hasEven : Async<bool> = numbers |> AsyncSeq.exists (fun n -> n % 2 = 0)
let allSmall : Async<bool> = numbers |> AsyncSeq.forall (fun n -> n < 100)
AsyncSeq.existsAsync and AsyncSeq.forallAsync accept async predicates.
head, last and firstOrDefault
AsyncSeq.head returns the first element, raising if the sequence is empty.
AsyncSeq.firstOrDefault returns a default value for empty sequences.
AsyncSeq.last and AsyncSeq.lastOrDefault do the same for the final element:
let strings = asyncSeq { yield! [ "hello"; "world" ] }
let firstWord : Async<string> = AsyncSeq.head strings
let lastWord : Async<string> = AsyncSeq.last strings
let safeFirst : Async<string> = AsyncSeq.firstOrDefault "none" strings
Collecting to a Collection
The toArrayAsync and toListAsync functions consume the entire sequence and materialise it
into an F# array or list. These are useful when you need random access or must pass the results
to code that expects a concrete collection:
let asArray : Async<int[]> = numbers |> AsyncSeq.toArrayAsync
let asList : Async<int list> = numbers |> AsyncSeq.toListAsync
toArraySynchronously and toListSynchronously do the same without wrapping in Async — they
block the calling thread until the sequence is exhausted. Only use these outside of async
contexts, e.g. in test code or scripts:
let syncArray : int[] = numbers |> AsyncSeq.toArraySynchronously
Aggregation
length
AsyncSeq.length counts the elements, returning Async<int64>:
let count : Async<int64> = numbers |> AsyncSeq.length
sumBy and sumByAsync
AsyncSeq.sumBy projects each element to a numeric type and sums the results:
let sumOfSquares : Async<int> =
numbers |> AsyncSeq.sumBy (fun n -> n * n)
AsyncSeq.sumByAsync is the same when the projection needs to perform async work:
let fetchScore (n: int) : Async<float> =
async { return float n * 0.5 } // placeholder
let totalScore : Async<float> =
numbers |> AsyncSeq.sumByAsync fetchScore
averageBy and averageByAsync
AsyncSeq.averageBy computes the arithmetic mean of a projected value:
let meanSquare : Async<float> =
numbers |> AsyncSeq.averageBy (fun n -> float (n * n))
AsyncSeq.averageByAsync accepts an async projection:
let meanScore : Async<float> =
numbers |> AsyncSeq.averageByAsync fetchScore
countBy
AsyncSeq.countBy counts how many elements share each key, returning an array of
(key, count) pairs:
let digitParity : Async<(string * int) array> =
numbers |> AsyncSeq.countBy (fun n -> if n % 2 = 0 then "even" else "odd")
AsyncSeq.countByAsync accepts an async projection when computing the key requires IO.
namespace FSharp
--------------------
namespace Microsoft.FSharp
namespace FSharp.Control
--------------------
namespace Microsoft.FSharp.Control
<summary> Builds an asynchronous sequence using the computation builder syntax </summary>
type Async = static member AsBeginEnd: computation: ('Arg -> Async<'T>) -> ('Arg * AsyncCallback * objnull -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit) static member AwaitEvent: event: IEvent<'Del,'T> * ?cancelAction: (unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate) static member AwaitIAsyncResult: iar: IAsyncResult * ?millisecondsTimeout: int -> Async<bool> static member AwaitTask: task: Task<'T> -> Async<'T> + 1 overload static member AwaitWaitHandle: waitHandle: WaitHandle * ?millisecondsTimeout: int -> Async<bool> static member CancelDefaultToken: unit -> unit static member Catch: computation: Async<'T> -> Async<Choice<'T,exn>> static member Choice: computations: Async<'T option> seq -> Async<'T option> static member FromBeginEnd: beginAction: (AsyncCallback * objnull -> IAsyncResult) * endAction: (IAsyncResult -> 'T) * ?cancelAction: (unit -> unit) -> Async<'T> + 3 overloads static member FromContinuations: callback: (('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T> ...
--------------------
type Async<'T>
static member Async.Sleep: millisecondsDueTime: int -> Async<unit>
module AsyncSeq from FSharp.Control
--------------------
type AsyncSeq<'T> = System.Collections.Generic.IAsyncEnumerable<'T>
<summary> An asynchronous sequence; equivalent to System.Collections.Generic.IAsyncEnumerable<'T>. Use the asyncSeq { ... } computation expression to create values, and the AsyncSeq module for combinators. </summary>
<summary> Iterates over the input sequence and calls the specified function for every value. </summary>
val int: value: 'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> = int
<summary> Iterates over the input sequence and calls the specified asynchronous function for every value. The input sequence will be asked for the next element after the processing of an element completes. </summary>
<summary> Iterates over the input sequence and calls the specified function for every value, passing along the index of that element. </summary>
<summary> Iterates over the input sequence and calls the specified asynchronous function for every value. Each action computation is started but not awaited before consuming the next item from the sequence, thereby iterating in parallel. </summary>
<summary> Iterates over the input sequence and calls the specified asynchronous function for every value. Each action computation is started but not awaited before consuming the next item from the sequence, thereby iterating in parallel with a specified degree of parallelism. </summary>
<summary> Asynchronously aggregate the elements of the input asynchronous sequence using the specified 'aggregation' function. </summary>
<summary> Asynchronously aggregate the elements of the input asynchronous sequence using the specified asynchronous 'aggregation' function. </summary>
val string: value: 'T -> string
--------------------
type string = System.String
<summary> Asynchronously reduce the elements of the input asynchronous sequence using the specified asynchronous 'reduction' function. Raises InvalidOperationException if the sequence is empty. </summary>
<summary> Asynchronously pick a value from a sequence based on the specified chooser function. Raises KeyNotFoundException if the chooser function can't find a matching key. </summary>
<summary> Asynchronously pick a value from a sequence based on the specified chooser function. </summary>
<summary> Asynchronously determine if there is a value in the sequence for which the predicate returns true </summary>
<summary> Asynchronously determine if the predicate returns true for all values in the sequence </summary>
<summary> Asynchronously returns the first element of the asynchronous sequence. Raises InvalidOperationException if the sequence is empty. </summary>
<summary> Asynchronously returns the last element of the asynchronous sequence. Raises InvalidOperationException if the sequence is empty, mirroring Seq.last. </summary>
<summary> Asynchronously returns the first element that was generated by the given asynchronous sequence (or the specified default value). </summary>
<summary> Creates an async computation which iterates the AsyncSeq and collects the output into an array. </summary>
<summary> Creates an async computation which iterates the AsyncSeq and collects the output into a list. </summary>
<summary> Synchronously iterates the AsyncSeq and collects the output into an array. </summary>
val int64: value: 'T -> int64 (requires member op_Explicit)
--------------------
type int64 = System.Int64
--------------------
type int64<'Measure> = int64
<summary> Asynchronously determine the number of elements in the sequence </summary>
<summary> Asynchronously sum the mapped elements of an asynchronous sequence using a synchronous projection. </summary>
val float: value: 'T -> float (requires member op_Explicit)
--------------------
type float = System.Double
--------------------
type float<'Measure> = float
<summary> Asynchronously sum the mapped elements of an asynchronous sequence using an asynchronous projection. </summary>
<summary> Asynchronously compute the average of the mapped elements of an asynchronous sequence using a synchronous projection. Raises InvalidArgumentException if the sequence is empty. </summary>
<summary> Asynchronously compute the average of the mapped elements of an asynchronous sequence using an asynchronous projection. Raises InvalidArgumentException if the sequence is empty. </summary>
<summary> Asynchronously count the elements of the input asynchronous sequence grouped by the result of the given key projection. Returns an array of (key, count) pairs. </summary>
FSharp.Control.AsyncSeq