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 FSharp
--------------------
namespace Microsoft.FSharp
namespace FSharp.Control
--------------------
namespace Microsoft.FSharp.Control
<summary> Builds an asynchronous task sequence based on <see cref="IAsyncEnumerable<'T>" /> using computation expression syntax. </summary>
val float: value: 'T -> float (requires member op_Explicit)
--------------------
type float = System.Double
--------------------
type float<'Measure> = float
<summary>Provides constants and static methods for trigonometric, logarithmic, and other common mathematical functions.</summary>
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(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 string: value: 'T -> string
--------------------
type string = System.String
<summary>Provides a mechanism for releasing unmanaged resources.</summary>
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<_>" />. </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<'T>" /> 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>
val int: value: 'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> = int
<summary> Initialize an empty task sequence. </summary>
val seq: sequence: 'T seq -> 'T seq
--------------------
type 'T seq = System.Collections.Generic.IEnumerable<'T>
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>
FSharp.Control.TaskSeq