Header menu logo FSharp.Control.TaskSeq

Generating Task Sequences

This page covers the main ways to create TaskSeq<'T> values: the taskSeq computation expression, factory functions such as TaskSeq.init and TaskSeq.unfold, and conversion functions that wrap existing collections.

open System.Threading.Tasks
open FSharp.Control

Computation Expression Syntax

taskSeq { ... } is a computation expression that lets you write asynchronous sequences using familiar F# constructs. Under the hood it compiles to a resumable state machine, so there is no allocation per element.

Yielding values

Use yield to emit a single value and yield! to splice in another sequence:

let helloWorld = taskSeq {
    yield "hello"
    yield "world"
}

let combined = taskSeq {
    yield! helloWorld
    yield "!"
}

Conditionals

if/then/else works just like in ordinary F#:

let evenNumbers = taskSeq {
    for i in 1..10 do
        if i % 2 = 0 then
            yield i
}

Match expressions

match expressions are fully supported:

type Shape =
    | Circle of radius: float
    | Rectangle of width: float * height: float

let areas = taskSeq {
    for shape in [ Circle 3.0; Rectangle(4.0, 5.0); Circle 1.5 ] do
        match shape with
        | Circle r -> yield System.Math.PI * r * r
        | Rectangle(w, h) -> yield w * h
}

For loops

for iterates over any seq<'T>/IEnumerable<'T> synchronously, or over another TaskSeq<'T> asynchronously:

let squaresOfList = taskSeq {
    for n in [ 1; 2; 3; 4; 5 ] do
        yield n * n
}

// Iterate another TaskSeq
let doubled = taskSeq {
    for n in squaresOfList do
        yield n * 2
}

While loops

while emits elements until a condition becomes false. Async operations can appear in the loop body:

let countdown = taskSeq {
    let mutable i = 5

    while i > 0 do
        yield i
        do! Task.Delay 100
        i <- i - 1
}

Awaiting tasks with let! and do!

Inside taskSeq { ... } you can await any Task<'T> with let! and any Task<unit> with do!:

let fetchData (url: string) : Task<string> =
    task { return $"data from {url}" } // placeholder

let results = taskSeq {
    for url in [ "https://example.com/a"; "https://example.com/b" ] do
        let! data = fetchData url
        yield data
}

Use bindings and try / with

use and use! dispose the resource when the sequence finishes or is abandoned. try/with and try/finally work as expected:

let withResource = taskSeq {
    use resource = { new System.IDisposable with member _.Dispose() = () } // placeholder
    yield 1
    yield 2
}

let withErrorHandling = taskSeq {
    try
        yield 1
        failwith "oops"
        yield 2
    with ex ->
        yield -1
}

init and initInfinite

TaskSeq.init count initializer generates count elements by calling initializer with the zero-based index:

// [| 0; 1; 4; 9; 16 |]
let squares : TaskSeq<int> = TaskSeq.init 5 (fun i -> i * i)

// With an async initializer
let asyncSquares : TaskSeq<int> =
    TaskSeq.initAsync 5 (fun i -> task { return i * i })

TaskSeq.initInfinite generates an unbounded sequence — use TaskSeq.take or TaskSeq.takeWhile to limit consumption:

let naturals : TaskSeq<int> = TaskSeq.initInfinite id

let first10 : TaskSeq<int> = naturals |> TaskSeq.take 10 // 0 .. 9

unfold

TaskSeq.unfold derives a sequence from a state value. Each call to the generator returns either None (end) or Some (element, nextState):

// 0, 1, 2, ... up to but not including 5
let counting : TaskSeq<int> =
    TaskSeq.unfold
        (fun state ->
            if state < 5 then
                Some(state, state + 1)
            else
                None)
        0

// Same with an async generator
let countingAsync : TaskSeq<int> =
    TaskSeq.unfoldAsync
        (fun state ->
            task {
                if state < 5 then
                    return Some(state, state + 1)
                else
                    return None
            })
        0

singleton, replicate and empty

let one : TaskSeq<string> = TaskSeq.singleton "hello"

let fives : TaskSeq<int> = TaskSeq.replicate 3 5 // 5, 5, 5

let nothing : TaskSeq<int> = TaskSeq.empty<int>

delay

TaskSeq.delay creates a sequence whose body is not evaluated until iteration starts. This is useful when the sequence depends on side-effectful initialisation:

let deferred =
    TaskSeq.delay (fun () ->
        taskSeq {
            printfn "sequence started"
            yield! [ 1; 2; 3 ]
        })

Converting from existing collections

All of these produce a TaskSeq<'T> that replays the source on each iteration:

let fromArray : TaskSeq<int> = TaskSeq.ofArray [| 1; 2; 3 |]
let fromList : TaskSeq<int> = TaskSeq.ofList [ 1; 2; 3 ]
let fromSeq : TaskSeq<int> = TaskSeq.ofSeq (seq { 1..3 })
let fromResizeArray : TaskSeq<int> = TaskSeq.ofResizeArray (System.Collections.Generic.List<int>([ 1; 2; 3 ]))

You can also wrap existing task or async collections. Note that wrapping a list of already started Tasks does not guarantee sequential execution—the tasks are already running:

// Sequence of not-yet-started task factories (safe)
let fromTaskSeq : TaskSeq<int> =
    TaskSeq.ofTaskSeq (seq { yield task { return 1 }; yield task { return 2 } })

// Sequence of asyncs (each started on demand as the sequence is consumed)
let fromAsyncSeq : TaskSeq<int> =
    TaskSeq.ofAsyncSeq (seq { yield async { return 1 }; yield async { return 2 } })
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 helloWorld: TaskSeq<string>
val taskSeq: TaskSeqBuilder
<summary> Builds an asynchronous task sequence based on <see cref="IAsyncEnumerable&lt;'T&gt;" /> using computation expression syntax. </summary>
val combined: TaskSeq<string>
val evenNumbers: TaskSeq<int>
val i: int
type Shape = | Circle of radius: float | Rectangle of width: float * height: float
Multiple items
val float: value: 'T -> float (requires member op_Explicit)

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

--------------------
type float<'Measure> = float
val areas: TaskSeq<float>
val shape: Shape
union case Shape.Circle: radius: float -> Shape
union case Shape.Rectangle: width: float * height: float -> Shape
val r: float
type Math = static member Abs: value: decimal -> decimal + 7 overloads static member Acos: d: float -> float static member Acosh: d: float -> float static member Asin: d: float -> float static member Asinh: d: float -> float static member Atan: d: float -> float static member Atan2: y: float * x: float -> float static member Atanh: d: float -> float static member BigMul: a: int * b: int -> int64 + 5 overloads static member BitDecrement: x: float -> float ...
<summary>Provides constants and static methods for trigonometric, logarithmic, and other common mathematical functions.</summary>
field System.Math.PI: float = 3.14159265359
val w: float
val h: float
val squaresOfList: TaskSeq<int>
val n: int
val doubled: TaskSeq<int>
val countdown: TaskSeq<int>
val mutable i: int
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.Delay(delay: System.TimeSpan) : Task
Task.Delay(millisecondsDelay: int) : Task
Task.Delay(delay: System.TimeSpan, timeProvider: System.TimeProvider) : Task
Task.Delay(delay: System.TimeSpan, cancellationToken: System.Threading.CancellationToken) : Task
Task.Delay(millisecondsDelay: int, cancellationToken: System.Threading.CancellationToken) : Task
Task.Delay(delay: System.TimeSpan, timeProvider: System.TimeProvider, cancellationToken: System.Threading.CancellationToken) : Task
val fetchData: url: string -> Task<string>
val url: string
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
val task: TaskBuilder
val results: TaskSeq<string>
val data: string
val withResource: TaskSeq<int>
val resource: System.IDisposable
type IDisposable = override Dispose: unit -> unit
<summary>Provides a mechanism for releasing unmanaged resources.</summary>
val withErrorHandling: TaskSeq<int>
val failwith: message: string -> 'T
val ex: exn
val squares: 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.init: count: int -> initializer: (int -> 'T) -> TaskSeq<'T>
val asyncSquares: TaskSeq<int>
static member TaskSeq.initAsync: count: int -> initializer: (int -> #Task<'T>) -> TaskSeq<'T>
val naturals: TaskSeq<int>
static member TaskSeq.initInfinite: initializer: (int -> 'T) -> TaskSeq<'T>
val id: x: 'T -> 'T
val first10: TaskSeq<int>
static member TaskSeq.take: count: int -> source: TaskSeq<'T> -> TaskSeq<'T>
val counting: TaskSeq<int>
static member TaskSeq.unfold: generator: ('State -> ('T * 'State) option) -> state: 'State -> TaskSeq<'T>
val state: int
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
val countingAsync: TaskSeq<int>
static member TaskSeq.unfoldAsync: generator: ('State -> Task<('T * 'State) option>) -> state: 'State -> TaskSeq<'T>
val one: TaskSeq<string>
static member TaskSeq.singleton: value: 'T -> TaskSeq<'T>
val fives: TaskSeq<int>
static member TaskSeq.replicate: count: int -> value: 'T -> TaskSeq<'T>
val nothing: TaskSeq<int>
val empty<'T> : TaskSeq<'T>
<summary> Initialize an empty task sequence. </summary>
val deferred: TaskSeq<int>
static member TaskSeq.delay: generator: (unit -> TaskSeq<'T>) -> TaskSeq<'T>
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
val fromArray: TaskSeq<int>
static member TaskSeq.ofArray: source: 'T array -> TaskSeq<'T>
val fromList: TaskSeq<int>
static member TaskSeq.ofList: source: 'T list -> TaskSeq<'T>
val fromSeq: TaskSeq<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 fromResizeArray: TaskSeq<int>
static member TaskSeq.ofResizeArray: source: ResizeArray<'T> -> TaskSeq<'T>
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>
val fromTaskSeq: TaskSeq<int>
static member TaskSeq.ofTaskSeq: source: #Task<'T> seq -> TaskSeq<'T>
val fromAsyncSeq: TaskSeq<int>
static member TaskSeq.ofAsyncSeq: source: Async<'T> seq -> TaskSeq<'T>
val async: AsyncBuilder

Type something to start searching.