如何在F#中异步下载网页并捕获错误
f#
f#-data
我一直试图找出惯用的 F# 方式来异步下载网页并处理任何错误/HTTP 失败代码,我认为这是我迄今为止最接近的尝试,但我在 Choice1Of2 行上遇到了类型错误
我想要
a) 了解为什么会失败(我仍在学习 F#)
b) 知道这是否是正确的方法/如何使这项工作正常进行,或者我是否完全走错了路
FS0001 This expression was expected to have type 'Async<Choice<string,exn>>' but here has type 'Choice<'a,'b>'
let fetchAsync url = async {
return! Async.Catch(async {
let! str = Http.AsyncRequestString(url)
return str })
}
let result = fetchAsync "http://www.example.bad"
match result with
| Choice1Of2 v -> logger.LogInformation("worked")
| Choice2Of2 ex -> logger.LogInformation("failed")
回答
a) 为什么失败
您的fetchAsync函数返回 anAsync<Choice<_>>并且您的模式匹配就好像该函数只返回Choice<_>。不幸的是,您不能在 Async 上进行模式匹配,因为这将是一个阻塞操作,而 Async 尝试远离的正是这种操作。
b) 处理这些事情的惯用方式。
但是,您可以做的是留在 Async 上下文中并处理其中的故障。F# 提供(至少)两种常见的方法来处理这个问题。例如,您可以使用允许您编写管道的异步辅助函数:
let fetchAsyncPipeline (url: string) =
FSharp.Data.Http.AsyncRequestString(url)
|> Async.Catch
|> Async.map (function
| Choice1Of2 v -> Ok v
| Choice2Of2 e -> Error e.Message)
不幸的是,尚未包含Async.map 。但是你可以像这样为自己定义:
namespace global
[<RequireQualifiedAccess>]
module Async =
let map f xA =
async {
let! x = xA
return f x
}
或者,您可以使用 F# 的计算表达式提供语法糖,以更命令式的方式编写上述内容:
let fetchAsyncCe (url: string) =
async {
try
return!
FSharp.Data.Http.AsyncRequestString(url)
|> Async.map Ok
with
| e -> return Error e.Message
}
在这两种解决方案中,我都将异常转换为 F# 的结果类型,我个人认为这是处理错误的最佳方式。
最后,如 brianberns 所示,您的表达式仅包含在Async类型中。但与C# 的 Task 不同,异步计算代表程序如何计算某些东西,但该程序尚未启动,您必须显式运行异步操作。一种方法是使用 Async.RunSynchronously:
Async.RunSynchronously (fetchAsyncCe "https://fsharpforfunandprofit.com/")
PS:您可能为了乐趣和利润而遇到 Scott Wlaschin 的出色F#,但如果您没有,您应该查看一下。就个人而言,我发现它是教您 F# 和一般函数式编程的最佳资源。