我找到了这个片段:
但我不仅正在使用可重复的功能,而且还使用异步功能,我想知道我是如何正确地使用这种类型的.我有一小块retryAsyncmonad,我想用它作为异步计算的替代品,但它包含重试逻辑,我想知道我是如何组合它们的?
type AsyncRetryBuilder(retries) =
member x.Return a = a // Enable 'return'
member x.ReturnFrom a = x.Run a
member x.Delay f = f // Gets wrapped body and returns it (as it is)
// so that the body is passed to 'Run'
member x.Bind expr f = async {
let! tmp = expr
return tmp
}
member x.Zero = failwith "Zero"
member x.Run (f : unit -> Async<_>) : _ =
let rec loop = function
| 0, Some(ex) -> raise ex
| n, _ ->
try
async { let! v = f()
return v }
with ex -> loop (n-1, Some(ex))
loop(retries, None)
let asyncRetry = AsyncRetryBuilder(4)
Run Code Online (Sandbox Code Playgroud)
消费代码是这样的:
module Queue =
let desc (nm : NamespaceManager) name = asyncRetry {
let! exists = Async.FromBeginEnd(name, nm.BeginQueueExists, nm.EndQueueExists)
let beginCreate = nm.BeginCreateQueue : string * AsyncCallback * obj -> IAsyncResult
return! if exists then Async.FromBeginEnd(name, nm.BeginGetQueue, nm.EndGetQueue)
else Async.FromBeginEnd(name, beginCreate, nm.EndCreateQueue)
}
let recv (client : MessageReceiver) timeout =
let bRecv = client.BeginReceive : TimeSpan * AsyncCallback * obj -> IAsyncResult
asyncRetry {
let! res = Async.FromBeginEnd(timeout, bRecv, client.EndReceive)
return res }
Run Code Online (Sandbox Code Playgroud)
错误是:
这个表达式应该有类型,
Async<'a>但这里有类型'b - >Async<'c>
您的Bind操作行为类似于正常Bind操作async,因此您的代码主要是重新实现(或包装)async.然而,你Return没有正确的类型(应该是'T -> Async<'T>)和你Delay也较正常不同Delay的async.一般来说,你应该从Bind和Return- 开始使用Run有点棘手,因为Run它用于包装整个foo { .. }块,所以它不会给你通常很好的可组合性.
在F#规范以及免费的第12章,从现实世界的函数式编程都显示常用的类型来实现这些操作时,应遵循,所以我不会重复在这里.
您的方法的主要问题是您尝试仅重试计算Run,但您所指的重试构建器尝试重试每个使用的单独操作let!.您的方法可能已足够,但如果是这种情况,您可以实现一个尝试运行正常Async<'T>和重试的函数:
let RetryRun count (work:Async<'T>) = async {
try
// Try to run the work
return! work
with e ->
// Retry if the count is larger than 0, otherwise fail
if count > 0 then return! RetryRun (count - 1) work
else return raise e }
Run Code Online (Sandbox Code Playgroud)
如果你真的想要实现一个计算构建器,它将隐式地尝试重试每一个异步操作,那么你可以编写类似下面的东西(它只是一个草图,但它应该指向正确的方向):
// We're working with normal Async<'T> and
// attempt to retry it until it succeeds, so
// the computation has type Async<'T>
type RetryAsyncBuilder() =
member x.ReturnFrom(comp) = comp // Just return the computation
member x.Return(v) = async { return v } // Return value inside async
member x.Delay(f) = async { return! f() } // Wrap function inside async
member x.Bind(work, f) =
async {
try
// Try to call the input workflow
let! v = work
// If it succeeds, try to do the rest of the work
return! f v
with e ->
// In case of exception, call Bind to try again
return! x.Bind(work, f) }
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
493 次 |
| 最近记录: |