Header menu logo FSharp.Control.TaskSeq

Consuming Task Sequences

All TaskSeq<'T> values are lazy — they produce elements only when actively consumed. This page covers all the ways to consume a task sequence: iteration with side effects, collection into arrays and lists, folding, aggregation, searching, and element access.

open System.Threading.Tasks
open FSharp.Control

let numbers : TaskSeq<int> = TaskSeq.ofSeq (seq { 1..5 })

Iterating with a for loop

Inside a task { ... } or taskSeq { ... } computation expression, you can iterate a TaskSeq<'T> with a plain for loop. The loop body may contain let! and do!:

task {
    for n in numbers do
        printfn "Got %d" n
}
|> Task.WaitAll

iter and iterAsync

TaskSeq.iter applies a synchronous side-effecting function to every element:

let printAll : Task<unit> = numbers |> TaskSeq.iter (printfn "item: %d")

TaskSeq.iterAsync awaits the returned task before consuming the next element — ideal for actions that themselves do IO, such as writing to a database:

let processItem (n: int) : Task<unit> =
    task { printfn "processing %d" n }

let processAll : Task<unit> = numbers |> TaskSeq.iterAsync processItem

iteri and iteriAsync

TaskSeq.iteri and TaskSeq.iteriAsync additionally pass the zero-based index to the action:

let printWithIndex : Task<unit> =
    numbers |> TaskSeq.iteri (fun i n -> printfn "[%d] %d" i n)

Collecting into arrays and lists

TaskSeq.toArrayAsync and TaskSeq.toListAsync consume the whole sequence into an in-memory collection. The blocking (synchronous) variants toArray, toList, and toSeq are also available when you need them in a synchronous context:

let arr : Task<int[]> = numbers |> TaskSeq.toArrayAsync
let lst : Task<int list> = numbers |> TaskSeq.toListAsync
let rz : Task<System.Collections.Generic.List<int>> = numbers |> TaskSeq.toResizeArrayAsync

// Blocking variants (avoid in async code)
let arrSync : int[] = numbers |> TaskSeq.toArray
let lstSync : int list = numbers |> TaskSeq.toList

fold and foldAsync

TaskSeq.fold threads an accumulator through the sequence, returning the final state:

let sum : Task<int> =
    numbers |> TaskSeq.fold (fun acc n -> acc + n) 0

let concat : Task<string> =
    TaskSeq.ofList [ "hello"; " "; "world" ]
    |> TaskSeq.fold (fun acc s -> acc + s) ""

TaskSeq.foldAsync is the same but the folder returns Task<'State>:

let sumAsync : Task<int> =
    numbers
    |> TaskSeq.foldAsync (fun acc n -> task { return acc + n }) 0

scan and scanAsync

TaskSeq.scan is like fold but emits each intermediate state as a new element. The output sequence has N + 1 elements when the input has N, because the initial state is also emitted:

let runningTotals : TaskSeq<int> =
    numbers |> TaskSeq.scan (fun acc n -> acc + n) 0

// yields 0, 1, 3, 6, 10, 15

reduce and reduceAsync

TaskSeq.reduce uses the first element as the initial state — there is no extra zero argument:

let product : Task<int> = numbers |> TaskSeq.reduce (fun acc n -> acc * n)

Aggregation: sum, average, min, max

Numeric aggregates follow the same pattern as the Seq module:

let total : Task<int> = numbers |> TaskSeq.sum

let avg : Task<float> =
    TaskSeq.ofList [ 1.0; 2.0; 3.0 ] |> TaskSeq.average

let biggest : Task<int> = numbers |> TaskSeq.max
let smallest : Task<int> = numbers |> TaskSeq.min

sumBy, averageBy, maxBy, and minBy apply a projection first. Async variants sumByAsync, averageByAsync, maxByAsync, and minByAsync are also available:

let sumOfSquares : Task<int> = numbers |> TaskSeq.sumBy (fun n -> n * n)

length and isEmpty

TaskSeq.length consumes the whole sequence. Use TaskSeq.lengthOrMax to avoid evaluating more than a known upper bound — useful for infinite sequences or for early termination:

let len : Task<int> = numbers |> TaskSeq.length // 5

let atMost3 : Task<int> = numbers |> TaskSeq.lengthOrMax 3 // 3 (stops early)

let empty : Task<bool> = numbers |> TaskSeq.isEmpty // false

TaskSeq.lengthBy counts elements satisfying a predicate in a single pass:

let countEvens : Task<int> = numbers |> TaskSeq.lengthBy (fun n -> n % 2 = 0)

Element access: head, last, item, exactlyOne

let first : Task<int> = numbers |> TaskSeq.head // 1
let last : Task<int> = numbers |> TaskSeq.last // 5
let third : Task<int> = numbers |> TaskSeq.item 2 // 3 (zero-based)
let only : Task<int> = TaskSeq.singleton 42 |> TaskSeq.exactlyOne // 42

// Safe "try" variants return None instead of throwing:
let tryFirst : Task<int option> = numbers |> TaskSeq.tryHead
let tryLast : Task<int option> = numbers |> TaskSeq.tryLast
let tryThird : Task<int option> = numbers |> TaskSeq.tryItem 2
let tryOnly : Task<int option> = TaskSeq.singleton 42 |> TaskSeq.tryExactlyOne

Searching: find, pick, contains, exists, forall

TaskSeq.find returns the first element satisfying a predicate, or throws if none is found. TaskSeq.tryFind returns None instead of throwing:

let firstEven : Task<int> = numbers |> TaskSeq.find (fun n -> n % 2 = 0)

let maybeEven : Task<int option> = numbers |> TaskSeq.tryFind (fun n -> n % 2 = 0)

TaskSeq.findIndex and TaskSeq.tryFindIndex return the zero-based index:

let indexOfFirst3 : Task<int> = numbers |> TaskSeq.findIndex (fun n -> n = 3)

TaskSeq.pick and TaskSeq.tryPick are like find but the predicate also projects to a new value — equivalent to a combined choose + head:

let firstSquareOver10 : Task<int option> =
    numbers
    |> TaskSeq.tryPick (fun n ->
        let sq = n * n
        if sq > 10 then Some sq else None)

TaskSeq.contains tests membership by equality. TaskSeq.exists tests with a predicate. TaskSeq.forall tests that all elements satisfy a predicate:

let has3 : Task<bool> = numbers |> TaskSeq.contains 3

let anyNegative : Task<bool> = numbers |> TaskSeq.exists (fun n -> n < 0)

let allPositive : Task<bool> = numbers |> TaskSeq.forall (fun n -> n > 0)

All search/predicate operations have Async variants (findAsync, existsAsync, forallAsync, etc.) that accept a predicate returning Task<bool>.

namespace System
namespace System.Threading
namespace System.Threading.Tasks
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
Multiple items
namespace FSharp.Control

--------------------
namespace Microsoft.FSharp.Control
val numbers: TaskSeq<int>
Multiple items
module TaskSeq from FSharp.Control.TaskSeqExtensions

--------------------
type TaskSeq = static member append: source1: TaskSeq<'T> -> source2: TaskSeq<'T> -> TaskSeq<'T> static member appendSeq: source1: TaskSeq<'T> -> source2: 'T seq -> TaskSeq<'T> static member box: source: TaskSeq<'T> -> TaskSeq<obj> static member cast: source: TaskSeq<obj> -> TaskSeq<'U> static member choose: chooser: ('T -> 'U option) -> source: TaskSeq<'T> -> TaskSeq<'U> static member chooseAsync: chooser: ('T -> #Task<'U option>) -> source: TaskSeq<'T> -> TaskSeq<'U> static member chunkBySize: chunkSize: int -> source: TaskSeq<'T> -> TaskSeq<'T array> static member collect: binder: ('T -> #TaskSeq<'U>) -> source: TaskSeq<'T> -> TaskSeq<'U> static member collectAsync: binder: ('T -> #Task<'TSeqU>) -> source: TaskSeq<'T> -> TaskSeq<'U> (requires 'TSeqU :> TaskSeq<'U>) static member collectSeq: binder: ('T -> #('U seq)) -> source: TaskSeq<'T> -> TaskSeq<'U> ...

--------------------
type TaskSeq<'T> = System.Collections.Generic.IAsyncEnumerable<'T>
<summary> Represents a task sequence and is the output of using the <paramref name="taskSeq{...}" /> computation expression from this library. It is an alias for <see cref="T:System.IAsyncEnumerable&lt;_&gt;" />. </summary>

--------------------
type TaskSeq<'Machine,'T (requires 'Machine :> IAsyncStateMachine and 'Machine :> IResumableStateMachine<TaskSeqStateMachineData<'T>>)> = inherit TaskSeqBase<'T> interface IValueTaskSource interface IValueTaskSource<bool> interface IAsyncStateMachine interface IAsyncEnumerable<'T> interface IAsyncEnumerator<'T> new: unit -> TaskSeq<'Machine,'T> member InitMachineData: ct: CancellationToken * machine: byref<'Machine> -> unit override MoveNextAsyncResult: unit -> ValueTask<bool>
<summary> Main implementation of generic <see cref="T:System.IAsyncEnumerable&lt;'T&gt;" /> and related interfaces, which forms the meat of the logic behind <see cref="taskSeq" /> computation expresssions. For use by this library only, should not be used directly in user code. Its operation depends highly on resumable state. </summary>

--------------------
new: unit -> TaskSeq<'Machine,'T>
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
static member TaskSeq.ofSeq: source: 'T seq -> TaskSeq<'T>
Multiple items
val seq: sequence: 'T seq -> 'T seq

--------------------
type 'T seq = System.Collections.Generic.IEnumerable<'T>
val task: TaskBuilder
val n: int
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
Multiple items
type Task = interface IAsyncResult interface IDisposable new: action: Action -> unit + 7 overloads member ConfigureAwait: continueOnCapturedContext: bool -> ConfiguredTaskAwaitable + 1 overload member ContinueWith: continuationAction: Action<Task,obj> * state: obj -> Task + 19 overloads member Dispose: unit -> unit member GetAwaiter: unit -> TaskAwaiter member RunSynchronously: unit -> unit + 1 overload member Start: unit -> unit + 1 overload member Wait: unit -> unit + 5 overloads ...
<summary>Represents an asynchronous operation.</summary>

--------------------
type Task<'TResult> = inherit Task new: ``function`` : Func<obj,'TResult> * state: obj -> unit + 7 overloads member ConfigureAwait: continueOnCapturedContext: bool -> ConfiguredTaskAwaitable<'TResult> + 1 overload member ContinueWith: continuationAction: Action<Task<'TResult>,obj> * state: obj -> Task + 19 overloads member GetAwaiter: unit -> TaskAwaiter<'TResult> member WaitAsync: cancellationToken: CancellationToken -> Task<'TResult> + 4 overloads member Result: 'TResult static member Factory: TaskFactory<'TResult>
<summary>Represents an asynchronous operation that can return a value.</summary>
<typeparam name="TResult">The type of the result produced by this <see cref="T:System.Threading.Tasks.Task`1" />.</typeparam>


--------------------
Task(action: System.Action) : Task
Task(action: System.Action, cancellationToken: System.Threading.CancellationToken) : Task
Task(action: System.Action, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj) : Task
Task(action: System.Action, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj, cancellationToken: System.Threading.CancellationToken) : Task
Task(action: System.Action<obj>, state: obj, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task

--------------------
Task(``function`` : System.Func<'TResult>) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj) : Task<'TResult>
Task(``function`` : System.Func<'TResult>, cancellationToken: System.Threading.CancellationToken) : Task<'TResult>
Task(``function`` : System.Func<'TResult>, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, cancellationToken: System.Threading.CancellationToken) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<'TResult>, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
Task.WaitAll([<System.ParamArray>] tasks: Task array) : unit
Task.WaitAll(tasks: System.ReadOnlySpan<Task>) : unit
Task.WaitAll(tasks: Task array, timeout: System.TimeSpan) : bool
Task.WaitAll(tasks: Task array, cancellationToken: System.Threading.CancellationToken) : unit
Task.WaitAll(tasks: Task array, millisecondsTimeout: int) : bool
Task.WaitAll(tasks: System.Collections.Generic.IEnumerable<Task>, ?cancellationToken: System.Threading.CancellationToken) : unit
Task.WaitAll(tasks: Task array, millisecondsTimeout: int, cancellationToken: System.Threading.CancellationToken) : bool
val printAll: Task<unit>
type unit = Unit
static member TaskSeq.iter: action: ('T -> unit) -> source: TaskSeq<'T> -> Task<unit>
val processItem: n: int -> Task<unit>
val processAll: Task<unit>
static member TaskSeq.iterAsync: action: ('T -> #Task<unit>) -> source: TaskSeq<'T> -> Task<unit>
val printWithIndex: Task<unit>
static member TaskSeq.iteri: action: (int -> 'T -> unit) -> source: TaskSeq<'T> -> Task<unit>
val i: int
val arr: Task<int array>
static member TaskSeq.toArrayAsync: source: TaskSeq<'T> -> Task<'T array>
val lst: Task<int list>
type 'T list = List<'T>
static member TaskSeq.toListAsync: source: TaskSeq<'T> -> Task<'T list>
val rz: Task<System.Collections.Generic.List<int>>
namespace System.Collections
namespace System.Collections.Generic
Multiple items
type List<'T> = interface ICollection<'T> interface IEnumerable<'T> interface IEnumerable interface IList<'T> interface IReadOnlyCollection<'T> interface IReadOnlyList<'T> interface ICollection interface IList new: unit -> unit + 2 overloads member Add: item: 'T -> unit ...
<summary>Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.</summary>
<typeparam name="T">The type of elements in the list.</typeparam>


--------------------
System.Collections.Generic.List() : System.Collections.Generic.List<'T>
System.Collections.Generic.List(collection: System.Collections.Generic.IEnumerable<'T>) : System.Collections.Generic.List<'T>
System.Collections.Generic.List(capacity: int) : System.Collections.Generic.List<'T>
static member TaskSeq.toResizeArrayAsync: source: TaskSeq<'T> -> Task<ResizeArray<'T>>
val arrSync: int array
static member TaskSeq.toArray: source: TaskSeq<'T> -> 'T array
val lstSync: int list
static member TaskSeq.toList: source: TaskSeq<'T> -> 'T list
val sum: Task<int>
static member TaskSeq.fold: folder: ('State -> 'T -> 'State) -> state: 'State -> source: TaskSeq<'T> -> Task<'State>
val acc: int
val concat: Task<string>
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
static member TaskSeq.ofList: source: 'T list -> TaskSeq<'T>
val acc: string
val s: string
val sumAsync: Task<int>
static member TaskSeq.foldAsync: folder: ('State -> 'T -> #Task<'State>) -> state: 'State -> source: TaskSeq<'T> -> Task<'State>
val runningTotals: TaskSeq<int>
static member TaskSeq.scan: folder: ('State -> 'T -> 'State) -> state: 'State -> source: TaskSeq<'T> -> TaskSeq<'State>
val product: Task<int>
static member TaskSeq.reduce: folder: ('T -> 'T -> 'T) -> source: TaskSeq<'T> -> Task<'T>
val total: Task<int>
val sum: source: TaskSeq<'T> -> Task<'T> (requires member (+))
<summary> Returns the sum of all elements of the task sequence. The elements must support the <c>+</c> operator, which is the case for all built-in numeric types. For sequences with a projection, use <see cref="TaskSeq.sumBy" />. </summary>
<param name="source">The input task sequence.</param>
<returns>The sum of all elements in the sequence, starting from <c>Unchecked.defaultof</c> as zero.</returns>
<exception cref="T:ArgumentNullException">Thrown when the input task sequence is null.</exception>
val avg: Task<float>
Multiple items
val float: value: 'T -> float (requires member op_Explicit)

--------------------
type float = System.Double

--------------------
type float<'Measure> = float
val average: source: TaskSeq<'T> -> Task<'T> (requires member (+) and member DivideByInt)
<summary> Returns the average of all elements of the task sequence. The elements must support the <c>+</c> operator and <c>DivideByInt</c>, which is the case for all built-in F# floating-point types. For sequences with a projection, consider using <see cref="TaskSeq.averageBy" />. </summary>
<param name="source">The input task sequence.</param>
<returns>The average of the elements in the sequence.</returns>
<exception cref="T:ArgumentNullException">Thrown when the input task sequence is null.</exception>
<exception cref="T:ArgumentException">Thrown when the input task sequence is empty.</exception>
val biggest: Task<int>
static member TaskSeq.max: source: TaskSeq<'T> -> Task<'T> (requires comparison)
val smallest: Task<int>
static member TaskSeq.min: source: TaskSeq<'T> -> Task<'T> (requires comparison)
val sumOfSquares: Task<int>
val sumBy: projection: ('T -> 'U) -> source: TaskSeq<'T> -> Task<'U> (requires member (+))
<summary> Returns the sum of the results generated by applying the <paramref name="projection" /> function to each element of the task sequence. The result type must support the <c>+</c> operator, which is the case for all built-in numeric types. If <paramref name="projection" /> is asynchronous, consider using <see cref="TaskSeq.sumByAsync" />. </summary>
<param name="projection">A function to transform items from the input sequence into summable values.</param>
<param name="source">The input task sequence.</param>
<returns>The sum of the projected values.</returns>
<exception cref="T:ArgumentNullException">Thrown when the input task sequence is null.</exception>
val len: Task<int>
static member TaskSeq.length: source: TaskSeq<'T> -> Task<int>
val atMost3: Task<int>
static member TaskSeq.lengthOrMax: max: int -> source: TaskSeq<'T> -> Task<int>
val empty: Task<bool>
type bool = System.Boolean
static member TaskSeq.isEmpty: source: TaskSeq<'T> -> Task<bool>
val countEvens: Task<int>
static member TaskSeq.lengthBy: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<int>
val first: Task<int>
static member TaskSeq.head: source: TaskSeq<'T> -> Task<'T>
val last: Task<int>
static member TaskSeq.last: source: TaskSeq<'T> -> Task<'T>
val third: Task<int>
static member TaskSeq.item: index: int -> source: TaskSeq<'T> -> Task<'T>
val only: Task<int>
static member TaskSeq.singleton: value: 'T -> TaskSeq<'T>
static member TaskSeq.exactlyOne: source: TaskSeq<'T> -> Task<'T>
val tryFirst: Task<int option>
type 'T option = Option<'T>
static member TaskSeq.tryHead: source: TaskSeq<'T> -> Task<'T option>
val tryLast: Task<int option>
static member TaskSeq.tryLast: source: TaskSeq<'T> -> Task<'T option>
val tryThird: Task<int option>
static member TaskSeq.tryItem: index: int -> source: TaskSeq<'T> -> Task<'T option>
val tryOnly: Task<int option>
static member TaskSeq.tryExactlyOne: source: TaskSeq<'T> -> Task<'T option>
val firstEven: Task<int>
static member TaskSeq.find: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<'T>
val maybeEven: Task<int option>
static member TaskSeq.tryFind: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<'T option>
val indexOfFirst3: Task<int>
static member TaskSeq.findIndex: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<int>
val firstSquareOver10: Task<int option>
static member TaskSeq.tryPick: chooser: ('T -> 'U option) -> source: TaskSeq<'T> -> Task<'U option>
val sq: int
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
val has3: Task<bool>
static member TaskSeq.contains: value: 'T -> source: TaskSeq<'T> -> Task<bool> (requires equality)
val anyNegative: Task<bool>
static member TaskSeq.exists: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<bool>
val allPositive: Task<bool>
static member TaskSeq.forall: predicate: ('T -> bool) -> source: TaskSeq<'T> -> Task<bool>

Type something to start searching.