How to use unit builders for non-generic task APIs
Use the unit builders when an API requires a non-generic Task or ValueTask.
The most important case is ValueTask: ValueTask<'T> is not a subtype of ValueTask.
For example, IAsyncDisposable.DisposeAsync must return non-generic ValueTask.
You can convert ValueTask<'T> with ValueTask.toUnit, but valueTaskUnit and vTaskUnit build the non-generic shape directly using the non-generic AsyncValueTaskMethodBuilder.
Implement IAsyncDisposable.DisposeAsync
DisposeAsync must return ValueTask, not ValueTask<unit>.
Use valueTaskUnit when the cleanup work is naturally written as a computation expression.
type BufferedWriter() =
let mutable flushed = false
member _.Flushed = flushed
member _.FlushAsync() =
valueTaskUnit {
do! Task.Delay 1
flushed <- true
}
interface IAsyncDisposable with
member this.DisposeAsync() = valueTaskUnit { do! this.FlushAsync() }
vTaskUnit is an alias for valueTaskUnit.
let flushWithAlias () : ValueTask = vTaskUnit { do! Task.Delay 1 }
Return non-generic Task
Use taskUnit when an API explicitly wants Task.
Task<'T> inherits from Task, but taskUnit states the return shape directly and uses the non-generic task method builder.
type Worker() =
member _.StopAsync(cancellationToken: CancellationToken) : Task =
taskUnit { do! Task.Delay(1, cancellationToken) }
Use backgroundTaskUnit when the API wants non-generic Task and the work should avoid the caller's synchronization context.
See Use background builders to avoid caller context for the scheduler behavior.
let writeAuditInBackground () : Task = backgroundTaskUnit { do! Task.Delay 1 }
Convert when you already have a generic ValueTask
If you already have a ValueTask<'T>, use ValueTask.toUnit to discard the result and return non-generic ValueTask.
Prefer valueTaskUnit when you are authoring the computation and know the API needs ValueTask.
let loadCount () = valueTask { return 42 }
let loadCountAsUnit () : ValueTask =
loadCount ()
|> ValueTask.toUnit
Run the examples
These calls make the samples complete and compiler-checked.
let writer = new BufferedWriter()
let disposed =
(writer :> IAsyncDisposable).DisposeAsync().AsTask().GetAwaiter().GetResult()
let flushed = writer.Flushed
let aliasResult = flushWithAlias().AsTask().GetAwaiter().GetResult()
let stopped = Worker().StopAsync(CancellationToken.None).GetAwaiter().GetResult()
let audit = writeAuditInBackground().GetAwaiter().GetResult()
let loadedAsUnit = loadCountAsUnit().AsTask().GetAwaiter().GetResult()
Choose the unit builder
API needs |
Use |
|---|---|
|
|
|
|
|
|
|
|
Use the generic builders when the API should return a meaningful value, such as Task<'T> or ValueTask<'T>.
type BufferedWriter = interface IAsyncDisposable new: unit -> BufferedWriter member FlushAsync: unit -> ValueTask member Flushed: bool
--------------------
new: unit -> BufferedWriter
<summary> Builds a non-generic ValueTask using computation expression syntax. </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: 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.Delay(millisecondsDelay: int) : Task
Task.Delay(delay: TimeSpan, timeProvider: TimeProvider) : Task
Task.Delay(delay: TimeSpan, cancellationToken: CancellationToken) : Task
Task.Delay(millisecondsDelay: int, cancellationToken: CancellationToken) : Task
Task.Delay(delay: TimeSpan, timeProvider: TimeProvider, cancellationToken: CancellationToken) : Task
<summary>Provides a mechanism for releasing unmanaged resources asynchronously.</summary>
[<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>
<summary> Alias for <see cref="F:IcedTasks.ValueTasksUnit.ValueTasksUnit.ValueTaskBuilder.valueTaskUnit" />. </summary>
type Worker = new: unit -> Worker member StopAsync: cancellationToken: CancellationToken -> Task
--------------------
new: unit -> Worker
[<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
<summary> Builds a taskUnit using computation expression syntax. </summary>
<summary> Builds a taskUnit using computation expression syntax which switches to execute on a background thread if not already doing so. </summary>
<summary> Builds a valueTask using computation expression syntax. </summary>
val toUnit: vtask: ValueTask<'T> -> ValueTask
<summary>Converts a <see cref="T:System.Threading.Tasks.ValueTask`1" /> to its non-generic counterpart.</summary>
<param name="vtask">The ValueTask whose result should be discarded.</param>
<typeparam name="'T">The result type to discard.</typeparam>
<returns>A non-generic ValueTask that completes when <paramref name="vtask" /> completes.</returns>
--------------------
val toUnit: vtask: ValueTask<'T> -> ValueTask
<summary>Converts a <see cref="T:System.Threading.Tasks.ValueTask`1" /> to its non-generic counterpart.</summary>
<param name="vtask">The ValueTask whose result should be discarded.</param>
<typeparam name="'T">The result type to discard.</typeparam>
<returns>A non-generic ValueTask that completes when <paramref name="vtask" /> completes.</returns>
<summary>Returns an empty <see cref="T:System.Threading.CancellationToken" /> value.</summary>
<returns>An empty cancellation token.</returns>
IcedTasks