FSharp.Core 11 为任务互操作、结果转换和有限并发添加函数。 将它们与现有 异步工作流 和 任务表达式配合使用;它们不需要运行时异步原型。
从异步执行 await .NET 操作
若要使用异步工作流的取消令牌启动.NET操作,请将工厂传递给Async.StartTaskImmediate:
open System.IO
let tryReadFile path =
async {
try
let! contents =
Async.StartTaskImmediate(fun ct -> File.ReadAllTextAsync(path, ct))
return Some contents
with :? FileNotFoundException ->
return None
}
读取的文件接收工作流的取消令牌。 缺少的文件返回 None;成功读取返回 Some contents。 工作流等待完成,包括取消和清理。
当已有类似任务的值时使用 Async.Await 。 这两个 API 直接传播出错任务的单个基础异常。 具有多个内部异常的聚合仍然是一个聚合。
Async.AwaitTask 保留其现有的异常包装行为。
| API | 支持的参数/结果形状 |
|---|---|
Async.Await |
Task<'T> -> Async<'T>;Task -> Async<unit> |
Async.Await |
ValueTask<'T> -> Async<'T>;ValueTask -> Async<unit> |
Async.Await |
使用 GetAwaiter 协议的内联任务类重载。 |
Async.StartTaskImmediate |
(CancellationToken -> Task<'T>) -> Async<'T>;非泛型 Task 返回 Async<unit>。 |
Async.StartTaskImmediate |
相应的 ValueTask、 ValueTask<'T>类似任务的工厂重载。 |
Task.startAsyncImmediate |
CancellationToken -> Async<'T> -> Task<'T> |
Async.RunSynchronouslyImmediate |
Async<'T> * ?cancellationToken: CancellationToken -> 'T |
Async.RunSynchronouslyImmediate 在调用线程上启动,与 Async.RunSynchronously 前台线程不同,以及用于完成的块。 它没有超时参数。 首选 UI 和请求线程上的异步组合。
将异步工作流公开为任务
若要向基于任务的调用方提供上述文件读取工作流,请接受取消令牌并通过 Task.startAsyncImmediate:
let tryReadFileTask (path: string) (ct: CancellationToken) =
tryReadFile path |> Task.startAsyncImmediate ct
桥返回 Task<string option> 并传播取消。 它在调用线程上开始,直到第一个挂起;以后的延续不需要使用该线程。
绑定并发
一个 Async<'T> 值描述延迟的工作。 任务通常表示已启动的操作。 若要限制资源使用,请将任务工作描述为工厂,而不是已运行的任务:
open System.IO
open System.Threading
let readFiles (paths: seq<string>) (ct: CancellationToken) =
paths
|> Seq.map (fun path ->
fun token -> File.ReadAllTextAsync(path, token))
|> Task.parallelLimit 4 ct
Task.parallelLimit 从并发计算的正限制开始,并按输入顺序返回结果。 启动顺序和完成顺序不指定。 对于大于 1 的并发性,每个工厂都会收到一个链接令牌,该令牌也表示同级故障;对于顺序执行,它直接接收提供的令牌。
发生一个计算错误时,会为其同级请求取消。 操作必须观察其令牌才能合作。 如果多个计算失败,则会传播一个异常;不依赖于哪个失败获胜。
| 函数 | 签名形状 |
|---|---|
Task.parallelLimit |
int -> CancellationToken -> seq<CancellationToken -> Task<'T>> -> Task<'T[]> |
Task.parallelDoLimit |
int -> CancellationToken -> seq<CancellationToken -> Task<unit>> -> Task<unit> |
Task.sequential |
CancellationToken -> seq<CancellationToken -> Task<'T>> -> Task<'T[]> |
Task.sequentialDo |
CancellationToken -> seq<CancellationToken -> Task<unit>> -> Task<unit> |
Async.parallelLimit |
int -> seq<Async<'T>> -> Async<'T[]> |
Async.parallelDoLimit |
int -> seq<Async<unit>> -> Async<unit> |
Async.sequentialDo |
seq<Async<unit>> -> Async<unit> |
这些 Async 函数流式传递工作流的环境取消令牌。 现有 Async.Parallel (包括其并发限制选项)保持 Async.Sequential 可用。 当只需要完成时,变量 Do 会公开结果 unit 。 并发限制不是速率限制程序:快速操作仍然可以每秒生成许多请求。
转换结果并从故障中恢复
和Microsoft.FSharp.Control.AsyncTaskValueTask模块公开这些限定的函数。
M<'T> 在形状列中表示相应的计算类型,而不是 F# 类型缩写。
| 限定的函数 | 形状 |
|---|---|
Async.result、Task.result、ValueTask.result |
'T -> M<'T> |
Async.map、Task.map、ValueTask.map |
('T -> 'U) -> M<'T> -> M<'U> |
Async.bind、Task.bind、ValueTask.bind |
('T -> M<'U>) -> M<'T> -> M<'U> |
Async.ignore<'T>、Task.ignore<'T>、ValueTask.ignore<'T> |
M<'T> -> M<unit>;需要显式输入类型参数。 |
Async.catchWith、Task.catchWith、ValueTask.catchWith |
(exn -> 'T) -> M<'T> -> M<'T> |
Async.catch、Task.catch、ValueTask.catch |
M<'T> -> M<Result<'T, exn>> |
Async.empty、Task.empty、ValueTask.empty |
M<unit> |
Task.ofValueTask |
ValueTask<'T> -> Task<'T> |
ValueTask.ofTask |
Task<'T> -> ValueTask<'T> |
例如,将文件的内容映射到长度,然后将读取失败表示为 Result:
open System.IO
open System.Threading
let fileLength (path: string) (ct: CancellationToken) =
File.ReadAllTextAsync(path, ct)
|> Task.map String.length
|> Task.catch
一个包含 Report 1 提供 Ok 8的文件。 缺少的文件提供包含的一个 ErrorFileNotFoundException。 若要放弃成功的值而不是转换它,请在调用 ignore时指定输入类型:
let completion: System.Threading.Tasks.Task<unit> =
Task.result 42 |> Task.ignore<int>
catchWith 为非取消失败提供替换值;它不运行另一个异步计算。
catch 返回 Ok 或 Error。 取消不会转换为错误值或传递给恢复处理程序。
Task.empty 是一个 Task<unit>,而不是非泛型 Task.CompletedTask。
与小写不同 Async.catch,现有 Async.Catch 返回值 Choice。 恢复处理提供的计算失败,而不是调用之前引发的同步异常。 异步保持延迟;任务仍然渴望。
ValueTask 和库可用性
ValueTask API 需要 netstandard2.1 FSharp.Core 11 或 net10.0 生成。 请参阅 核心目标选择。
观察 ValueTask 单消耗协定:使用每个值一次,而不是通过单独的等待或管道。 对于重复的等待,请调用 Task.ofValueTask 一次,只重复使用生成的任务。