How to use AsyncEx for .NET async interop
Use asyncEx when you want F# Async<'T> semantics and also need to bind modern .NET async shapes directly.
The workflow still returns Async<'T>, so you can run it with the usual Async APIs.
Open IcedTasks.AsyncEx to bring the asyncEx builder into scope.
open System
open System.Collections.Generic
open System.Threading
open System.Threading.Tasks
open IcedTasks.AsyncEx
Bind Task, ValueTask, and awaitables
Inside asyncEx, let! and do! can bind Task<'T>, Task, ValueTask<'T>, ValueTask, and awaitable values such as Task.Yield().
let getTaskValue () = Task.FromResult 21
let getValueTaskValue () = ValueTask<int> 21
let bindDotnetAsyncShapes =
asyncEx {
let! left = getTaskValue () // Uses Async.AwaitTask under the hood
let! right = getValueTaskValue () // Uses Async.AwaitValueTask under the hood
do! Task.Yield()
return
left
+ right
}
let answer = Async.RunSynchronously bindDotnetAsyncShapes
Dispose IAsyncDisposable values
Use use for resources that implement IAsyncDisposable. Disposal runs when the workflow completes, raises, or is cancelled.
type TrackedAsyncDisposable(disposed: bool ref) =
interface IAsyncDisposable with
member _.DisposeAsync() =
disposed.Value <- true
ValueTask()
let useAsyncDisposable =
asyncEx {
let disposed = ref false
use _resource = new TrackedAsyncDisposable(disposed)
return disposed
}
let disposedAfterWorkflow =
useAsyncDisposable
|> Async.RunSynchronously
|> fun disposed -> disposed.Value
Iterate IAsyncEnumerable<'T>
Use for directly with an IAsyncEnumerable<'T>.
let numbers: IAsyncEnumerable<int> =
{ new IAsyncEnumerable<int> with
member _.GetAsyncEnumerator(cancellationToken: CancellationToken) =
let values = [|
1
2
3
|]
let mutable index = -1
{ new IAsyncEnumerator<int> with
member _.Current = values[index]
member _.MoveNextAsync() =
cancellationToken.ThrowIfCancellationRequested()
index <- index + 1
ValueTask<bool>(index < values.Length)
member _.DisposeAsync() = ValueTask()
}
}
let sumAsyncEnumerable =
asyncEx {
let mutable total = 0
for number in numbers do
total <-
total
+ number
return total
}
let total = Async.RunSynchronously sumAsyncEnumerable
Shadow async when you want AsyncEx everywhere in a file
Open IcedTasks.Polyfill.Async when you want async { ... } to use the AsyncEx builder.
Use this sparingly because it intentionally shadows the FSharp.Core async builder in the current scope.
module PolyfillExample =
open IcedTasks.Polyfill.Async
let workflow =
async {
let! value = Task.FromResult 42
use _resource = new TrackedAsyncDisposable(ref false)
return value
}
let result = Async.RunSynchronously workflow
namespace System
namespace System.Collections
namespace System.Collections.Generic
namespace System.Threading
namespace System.Threading.Tasks
namespace IcedTasks
namespace IcedTasks.AsyncEx
val getTaskValue: unit -> Task<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: Action) : Task
Task(action: Action, cancellationToken: CancellationToken) : Task
Task(action: Action, creationOptions: TaskCreationOptions) : Task
Task(action: Action<obj>, state: obj) : Task
Task(action: Action, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task
Task(action: Action<obj>, state: obj, cancellationToken: CancellationToken) : Task
Task(action: Action<obj>, state: obj, creationOptions: TaskCreationOptions) : Task
Task(action: Action<obj>, state: obj, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task
--------------------
Task(``function`` : Func<'TResult>) : Task<'TResult>
Task(``function`` : Func<obj,'TResult>, state: obj) : Task<'TResult>
Task(``function`` : Func<'TResult>, cancellationToken: CancellationToken) : Task<'TResult>
Task(``function`` : Func<'TResult>, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : Func<obj,'TResult>, state: obj, cancellationToken: CancellationToken) : Task<'TResult>
Task(``function`` : Func<obj,'TResult>, state: obj, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : Func<'TResult>, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : Func<obj,'TResult>, state: obj, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
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: Action) : Task
Task(action: Action, cancellationToken: CancellationToken) : Task
Task(action: Action, creationOptions: TaskCreationOptions) : Task
Task(action: Action<obj>, state: obj) : Task
Task(action: Action, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task
Task(action: Action<obj>, state: obj, cancellationToken: CancellationToken) : Task
Task(action: Action<obj>, state: obj, creationOptions: TaskCreationOptions) : Task
Task(action: Action<obj>, state: obj, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task
--------------------
Task(``function`` : Func<'TResult>) : Task<'TResult>
Task(``function`` : Func<obj,'TResult>, state: obj) : Task<'TResult>
Task(``function`` : Func<'TResult>, cancellationToken: CancellationToken) : Task<'TResult>
Task(``function`` : Func<'TResult>, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : Func<obj,'TResult>, state: obj, cancellationToken: CancellationToken) : Task<'TResult>
Task(``function`` : Func<obj,'TResult>, state: obj, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : Func<'TResult>, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : Func<obj,'TResult>, state: obj, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
Task.FromResult<'TResult>(result: 'TResult) : Task<'TResult>
val getValueTaskValue: unit -> ValueTask<int>
Multiple items
[<Struct>] type ValueTask = new: source: IValueTaskSource * token: int16 -> unit + 1 overload member AsTask: unit -> Task member ConfigureAwait: continueOnCapturedContext: bool -> ConfiguredValueTaskAwaitable member Equals: obj: obj -> bool + 1 overload member GetAwaiter: unit -> ValueTaskAwaiter member GetHashCode: unit -> int member Preserve: unit -> ValueTask static member (<>) : left: ValueTask * right: ValueTask -> bool static member (=) : left: ValueTask * right: ValueTask -> bool static member FromCanceled: cancellationToken: CancellationToken -> ValueTask + 1 overload ...
<summary>Provides an awaitable result of an asynchronous operation.</summary>
--------------------
[<Struct>] type ValueTask<'TResult> = new: source: IValueTaskSource<'TResult> * token: int16 -> unit + 2 overloads member AsTask: unit -> Task<'TResult> member ConfigureAwait: continueOnCapturedContext: bool -> ConfiguredValueTaskAwaitable<'TResult> member Equals: obj: obj -> bool + 1 overload member GetAwaiter: unit -> ValueTaskAwaiter<'TResult> member GetHashCode: unit -> int member Preserve: unit -> ValueTask<'TResult> member ToString: unit -> string static member (<>) : left: ValueTask<'TResult> * right: ValueTask<'TResult> -> bool static member (=) : left: ValueTask<'TResult> * right: ValueTask<'TResult> -> bool ...
<summary>Provides a value type that wraps a <see cref="T:System.Threading.Tasks.Task`1" /> and a <typeparamref name="TResult" />, only one of which is used.</summary>
<typeparam name="TResult">The result.</typeparam>
--------------------
ValueTask ()
ValueTask(task: Task) : ValueTask
ValueTask(source: Sources.IValueTaskSource, token: int16) : ValueTask
--------------------
ValueTask ()
ValueTask(task: Task<'TResult>) : ValueTask<'TResult>
ValueTask(result: 'TResult) : ValueTask<'TResult>
ValueTask(source: Sources.IValueTaskSource<'TResult>, token: int16) : ValueTask<'TResult>
[<Struct>] type ValueTask = new: source: IValueTaskSource * token: int16 -> unit + 1 overload member AsTask: unit -> Task member ConfigureAwait: continueOnCapturedContext: bool -> ConfiguredValueTaskAwaitable member Equals: obj: obj -> bool + 1 overload member GetAwaiter: unit -> ValueTaskAwaiter member GetHashCode: unit -> int member Preserve: unit -> ValueTask static member (<>) : left: ValueTask * right: ValueTask -> bool static member (=) : left: ValueTask * right: ValueTask -> bool static member FromCanceled: cancellationToken: CancellationToken -> ValueTask + 1 overload ...
<summary>Provides an awaitable result of an asynchronous operation.</summary>
--------------------
[<Struct>] type ValueTask<'TResult> = new: source: IValueTaskSource<'TResult> * token: int16 -> unit + 2 overloads member AsTask: unit -> Task<'TResult> member ConfigureAwait: continueOnCapturedContext: bool -> ConfiguredValueTaskAwaitable<'TResult> member Equals: obj: obj -> bool + 1 overload member GetAwaiter: unit -> ValueTaskAwaiter<'TResult> member GetHashCode: unit -> int member Preserve: unit -> ValueTask<'TResult> member ToString: unit -> string static member (<>) : left: ValueTask<'TResult> * right: ValueTask<'TResult> -> bool static member (=) : left: ValueTask<'TResult> * right: ValueTask<'TResult> -> bool ...
<summary>Provides a value type that wraps a <see cref="T:System.Threading.Tasks.Task`1" /> and a <typeparamref name="TResult" />, only one of which is used.</summary>
<typeparam name="TResult">The result.</typeparam>
--------------------
ValueTask ()
ValueTask(task: Task) : ValueTask
ValueTask(source: Sources.IValueTaskSource, token: int16) : ValueTask
--------------------
ValueTask ()
ValueTask(task: Task<'TResult>) : ValueTask<'TResult>
ValueTask(result: 'TResult) : ValueTask<'TResult>
ValueTask(source: Sources.IValueTaskSource<'TResult>, token: int16) : ValueTask<'TResult>
Multiple items
val int: value: 'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> = int
val int: value: 'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> = int
val bindDotnetAsyncShapes: Async<int>
val asyncEx: AsyncExBuilder
<summary>Builds an asynchronous workflow using computation expression syntax.</summary>
<remarks> The difference between the AsyncBuilder and AsyncExBuilder is follows: <list type="bullet"><item><description>Allows <c>use</c> on <see cref="T:System.IAsyncDisposable">System.IAsyncDisposable</see></description></item><item><description>Allows <c>let!</c> for Tasks, ValueTasks, and any Awaitable Type</description></item><item><description>When Tasks throw exceptions they will use the behavior described in <see href="https://github.com/fsharp/fslang-suggestions/issues/840">Async.Await overload (esp. AwaitTask without throwing AggregateException)</see></description></item><item><description>Allow <c>for</c> on <see cref="T:System.Collections.Generic.IAsyncEnumerable`1">System.Collections.Generic.IAsyncDisposable</see></description></item></list></remarks>
<summary>Builds an asynchronous workflow using computation expression syntax.</summary>
<remarks> The difference between the AsyncBuilder and AsyncExBuilder is follows: <list type="bullet"><item><description>Allows <c>use</c> on <see cref="T:System.IAsyncDisposable">System.IAsyncDisposable</see></description></item><item><description>Allows <c>let!</c> for Tasks, ValueTasks, and any Awaitable Type</description></item><item><description>When Tasks throw exceptions they will use the behavior described in <see href="https://github.com/fsharp/fslang-suggestions/issues/840">Async.Await overload (esp. AwaitTask without throwing AggregateException)</see></description></item><item><description>Allow <c>for</c> on <see cref="T:System.Collections.Generic.IAsyncEnumerable`1">System.Collections.Generic.IAsyncDisposable</see></description></item></list></remarks>
val left: int
val right: int
Task.Yield() : Runtime.CompilerServices.YieldAwaitable
val answer: int
Multiple items
type Async = static member AsBeginEnd: computation: ('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> 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 * obj -> 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>
type Async = static member AsBeginEnd: computation: ('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> 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 * obj -> 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.RunSynchronously: computation: Async<'T> * ?timeout: int * ?cancellationToken: CancellationToken -> 'T
Multiple items
type TrackedAsyncDisposable = interface IAsyncDisposable new: disposed: bool ref -> TrackedAsyncDisposable
--------------------
new: disposed: bool ref -> TrackedAsyncDisposable
type TrackedAsyncDisposable = interface IAsyncDisposable new: disposed: bool ref -> TrackedAsyncDisposable
--------------------
new: disposed: bool ref -> TrackedAsyncDisposable
val disposed: bool ref
type bool = Boolean
Multiple items
val ref: value: 'T -> 'T ref
--------------------
type 'T ref = Ref<'T>
val ref: value: 'T -> 'T ref
--------------------
type 'T ref = Ref<'T>
type IAsyncDisposable =
override DisposeAsync: unit -> ValueTask
<summary>Provides a mechanism for releasing unmanaged resources asynchronously.</summary>
<summary>Provides a mechanism for releasing unmanaged resources asynchronously.</summary>
property Ref.Value: bool with get, set
val useAsyncDisposable: Async<bool ref>
val _resource: TrackedAsyncDisposable
val disposedAfterWorkflow: bool
val numbers: IAsyncEnumerable<int>
type IAsyncEnumerable<'T> =
override GetAsyncEnumerator: ?cancellationToken: CancellationToken -> IAsyncEnumerator<'T>
<summary>Exposes an enumerator that provides asynchronous iteration over values of a specified type.</summary>
<typeparam name="T">The type of values to enumerate.</typeparam>
<summary>Exposes an enumerator that provides asynchronous iteration over values of a specified type.</summary>
<typeparam name="T">The type of values to enumerate.</typeparam>
val cancellationToken: CancellationToken
Multiple items
[<Struct>] type CancellationToken = new: canceled: bool -> unit member Equals: other: obj -> bool + 1 overload member GetHashCode: unit -> int member Register: callback: Action -> CancellationTokenRegistration + 4 overloads member ThrowIfCancellationRequested: unit -> unit member UnsafeRegister: callback: Action<obj,CancellationToken> * state: obj -> CancellationTokenRegistration + 1 overload static member (<>) : left: CancellationToken * right: CancellationToken -> bool static member (=) : left: CancellationToken * right: CancellationToken -> bool member CanBeCanceled: bool member IsCancellationRequested: bool ...
<summary>Propagates notification that operations should be canceled.</summary>
--------------------
CancellationToken ()
CancellationToken(canceled: bool) : CancellationToken
[<Struct>] type CancellationToken = new: canceled: bool -> unit member Equals: other: obj -> bool + 1 overload member GetHashCode: unit -> int member Register: callback: Action -> CancellationTokenRegistration + 4 overloads member ThrowIfCancellationRequested: unit -> unit member UnsafeRegister: callback: Action<obj,CancellationToken> * state: obj -> CancellationTokenRegistration + 1 overload static member (<>) : left: CancellationToken * right: CancellationToken -> bool static member (=) : left: CancellationToken * right: CancellationToken -> bool member CanBeCanceled: bool member IsCancellationRequested: bool ...
<summary>Propagates notification that operations should be canceled.</summary>
--------------------
CancellationToken ()
CancellationToken(canceled: bool) : CancellationToken
val values: int array
val mutable index: int
type IAsyncEnumerator<'T> =
inherit IAsyncDisposable
override MoveNextAsync: unit -> ValueTask<bool>
member Current: 'T
<summary>Supports a simple asynchronous iteration over a generic collection.</summary>
<typeparam name="T">The type of objects to enumerate.</typeparam>
<summary>Supports a simple asynchronous iteration over a generic collection.</summary>
<typeparam name="T">The type of objects to enumerate.</typeparam>
CancellationToken.ThrowIfCancellationRequested() : unit
property Array.Length: int with get
<summary>Gets the total number of elements in all the dimensions of the <see cref="T:System.Array" />.</summary>
<exception cref="T:System.OverflowException">The array is multidimensional and contains more than <see cref="F:System.Int32.MaxValue">Int32.MaxValue</see> elements.</exception>
<returns>The total number of elements in all the dimensions of the <see cref="T:System.Array" />; zero if there are no elements in the array.</returns>
<summary>Gets the total number of elements in all the dimensions of the <see cref="T:System.Array" />.</summary>
<exception cref="T:System.OverflowException">The array is multidimensional and contains more than <see cref="F:System.Int32.MaxValue">Int32.MaxValue</see> elements.</exception>
<returns>The total number of elements in all the dimensions of the <see cref="T:System.Array" />; zero if there are no elements in the array.</returns>
val sumAsyncEnumerable: Async<int>
val mutable total: int
val number: int
val total: int
module PolyfillExample
from Use-AsyncEx-for-Dotnet-Async-Interop
namespace IcedTasks.Polyfill
namespace IcedTasks.Polyfill.Async
val workflow: Async<int>
val async: AsyncExBuilder
<summary> Builds an asynchronous workflow using computation expression syntax. <b>NOTE:</b> This is the AsyncExBuilder defined in IcedTasks.</summary>
<remarks> The difference between the AsyncBuilder and AsyncExBuilder is follows: <list type="bullet"><item><description>Allows <c>use</c> on <see cref="T:System.IAsyncDisposable">System.IAsyncDisposable</see></description></item><item><description>Allows <c>let!</c> for Tasks, ValueTasks, and any Awaitable Type</description></item><item><description>When Tasks throw exceptions they will use the behavior described in <see href="https://github.com/fsharp/fslang-suggestions/issues/840">Async.Await overload (esp. AwaitTask without throwing AggregateException)</see></description></item><item><description>Allow <c>for</c> on <see cref="T:System.Collections.Generic.IAsyncEnumerable`1">System.Collections.Generic.IAsyncDisposable</see></description></item></list></remarks>
<summary> Builds an asynchronous workflow using computation expression syntax. <b>NOTE:</b> This is the AsyncExBuilder defined in IcedTasks.</summary>
<remarks> The difference between the AsyncBuilder and AsyncExBuilder is follows: <list type="bullet"><item><description>Allows <c>use</c> on <see cref="T:System.IAsyncDisposable">System.IAsyncDisposable</see></description></item><item><description>Allows <c>let!</c> for Tasks, ValueTasks, and any Awaitable Type</description></item><item><description>When Tasks throw exceptions they will use the behavior described in <see href="https://github.com/fsharp/fslang-suggestions/issues/840">Async.Await overload (esp. AwaitTask without throwing AggregateException)</see></description></item><item><description>Allow <c>for</c> on <see cref="T:System.Collections.Generic.IAsyncEnumerable`1">System.Collections.Generic.IAsyncDisposable</see></description></item></list></remarks>
val value: int
val result: int
IcedTasks