FSharp.Core 11 新增了任務互通、結果轉換及有界並行等功能。 將它們與現有 的非同步工作流程 和 任務表達式一起使用;它們不需要執行時非同步原型。
等待非同步的 .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 從最多同時計算的正極限開始,並返回結果為輸入順序。 起始順序與完成順序則未明確說明。 當並發超過一時,每個工廠會收到一個連結的代幣,該代幣同時表示兄弟故障;在序列執行時,它會直接接收所提供的代幣。
當一個計算錯誤時,會請求其同胞的取消。 操作必須觀察其標記才能合作。 若多次計算失敗,會傳遞一個例外;不要依賴哪個失敗會贏。
| 函數 | 標誌性形狀 |
|---|---|
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.Async、 Task和 ValueTask 模組揭示了這些限定函數。
M<'T> 在 shape 欄位中,代表對應的計算類型,而非 F# 類型的縮寫。
| 合格職能 | Shape |
|---|---|
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。 缺失的檔案會得到 Error 包含 的 FileNotFoundException。 若要捨棄成功取值而非轉換,請在呼叫 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.1net10.0建置 FSharp.Core 11。 詳見 核心目標選擇。
請遵守 ValueTask 的單一消費合約:每個價值只消耗一次,不要透過分開的等待或管線。 對於重複等待,請先呼叫 Task.ofValueTask 一次,並只重複使用產生的任務。