[英]Why do I have to use (async method).result instead of await (async method)?
[英]Why do I have to wrap an Async<T> into another async workflow and let! it?
我試圖理解 F# 中的異步工作流,但我發現了一個我真的不理解的部分。
以下代碼工作正常:
let asynWorkflow = async{
let! result = Stream.TryOpenAsync(partition) |> Async.AwaitTask
return result
}
let stream = Async.RunSynchronously asynWorkflow
|> fun openResult -> if openResult.Found then openResult.Stream else Stream(partition)
我定義了一個異步工作流,其中 TryOpenAsync 返回Task<StreamOpenResult>
類型。 我使用 Async.AwaitTask 將其轉換為Async<StreamOpenResult>
。 (支線任務:“Await”Task?它不等待它只是轉換它,是嗎?我認為它與 Task.Wait 或 await 關鍵字無關)。 我用let!
“等待”它let!
並將其歸還。 要啟動工作流,我使用 RunSynchronously 它應該啟動工作流並返回結果(綁定它)。 在結果上,我檢查是否找到了流。
但現在我的第一個問題。 為什么我必須將 TryOpenAsync 調用包裝在另一個異步計算中,然后讓! (“等待”)嗎? 例如以下代碼不起作用:
let asynWorkflow = Stream.TryOpenAsync(partition) |> Async.AwaitTask
let stream = Async.RunSynchronously asynWorkflow
|> fun openResult -> if openResult.Found then openResult.Stream else Stream(partition)
我認為 AwaitTask 使它成為Async<T>
並且RunSynchronously
應該啟動它。 然后使用結果。 我想念什么?
我的第二個問題是為什么有任何“Async.Let!” 功能可用? 也許是因為它不起作用或更好為什么它不適用於以下代碼?
let ``let!`` task = async{
let! result = task |> Async.AwaitTask
return result
}
let stream = Async.RunSynchronously ( ``let!`` (Stream.TryOpenAsync(partition)) )
|> fun openResult -> if openResult.Found then openResult.Stream else Stream(partition)
我只是將 TryOpenAsync 作為參數插入,但它不起作用。 說不起作用我的意思是整個 FSI 將掛起。 所以它與我的異步/“等待”有關。
--- 更新:
FSI 中工作代碼的結果:
>
Real: 00:00:00.051, CPU: 00:00:00.031, GC gen0: 0, gen1: 0, gen2: 0
val asynWorkflow : Async<StreamOpenResult>
val stream : Stream
FSI 中代碼無效的結果:
>
並且您無法再在 FSI 中執行任何操作
--- 更新 2
我正在使用 Streamstone。 這里是 C# 示例: https : //github.com/yevhen/Streamstone/blob/master/Source/Example/Scenarios/S04_Write_to_stream.cs
這里是 Stream.TryOpenAsync: https : //github.com/yevhen/Streamstone/blob/master/Source/Streamstone/Stream.Api.cs#L192
如果不知道Stream
和partition
是什么以及它們是如何工作的,我無法告訴您為什么第二個示例不起作用。
但是,我想借此機會指出,這兩個示例並不嚴格等效。
F# async
有點像一個“食譜”。 當您編寫async { ... }
,結果計算只是坐在那里,實際上並沒有做任何事情。 它更像是聲明一個函數而不是發出一個命令。 只有當您通過調用Async.RunSynchronously
或Async.Start
類的東西“啟動”它時,它才會真正運行。 一個推論是,您可以多次啟動同一個異步工作流,並且每次都將是一個新的工作流。 與IEnumerable
工作方式非常相似。
另一方面,C# Task
更像是對已經在運行的異步計算的“引用”。 一旦您調用Stream.TryOpenAsync(partition)
,計算就會開始,並且不可能在任務實際開始之前獲取Task
實例。 您可以多次await
生成的Task
,但每次await
都不會導致重新嘗試打開流。 只有第一個await
才會真正等待任務完成,隨后的每個await
只會返回相同的記住結果。
在 async/reactive 行話中,F# async
被稱為“冷”,而 C# Task
被稱為“熱”。
第二個代碼塊看起來應該對我有用。 如果我為Stream
和StreamOpenResult
提供虛擬實現,它確實會運行它。
您應該盡可能避免使用Async.RunSynchronously
因為它違背了異步的目的。 將所有這些代碼放在一個更大的async
塊中,然后您就可以訪問StreamOpenResult
:
async {
let! openResult = Stream.TryOpenAsync(partition) |> Async.AwaitTask
let stream = if openResult.Found then openResult.Stream else Stream(partition)
return () // now do something with the stream
}
您可能需要將Async.Start
或Async.RunSynchronously
放在程序的最外邊緣才能實際運行它,但最好是有async
(或將其轉換為Task
)並將其傳遞給其他一些代碼(例如一個網絡框架)可以以非阻塞方式調用它。
不是我想用另一個問題來回答你的問題,而是:你為什么要寫這樣的代碼? 這可能有助於理解它。 為什么不只是:
let asyncWorkflow = async {
let! result = Stream.TryOpenAsync(partition) |> Async.AwaitTask
if result.Found then return openResult.Stream else return Stream(partition) }
創建異步工作流只是為了立即對其調用RunSynchronously
什么意義——它類似於在Task
上調用.Result
它只是阻塞當前線程,直到工作流返回。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.