如何在 F# 中异步下载网页并捕获错误
How do I asynchronously download a web page in F# and catch errors
我一直在尝试找出惯用的 F# 方式来异步下载网页并处理任何 errors/HTTP 故障代码,我认为这是迄今为止我最接近的尝试,但我遇到了类型错误在 Choice1Of2 行
我愿意
a) 理解失败的原因(我还在学习 F#)
b) 知道这是否是正确的 approach/how 来完成这项工作,或者我是否完全走错了路
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")
无法编译的原因是因为您实际上从未 运行 fetchAsync
创建的计算。像这样的东西应该可以代替:
let fetchAsync url =
Http.AsyncRequestString(url)
|> Async.Catch
|> Async.RunSynchronously
再过几个小时喝了很多咖啡后,我有了这个
let fetchAsync2 (url: string) =
async { return! Async.Catch(Http.AsyncRequestString(url)) }
|> Async.RunSynchronously
let result = fetchAsync2 url
match result with
| Choice1Of2 html -> html
| Choice2Of2 error -> error.Message
这段代码按照我想要的方式工作,但我会留下这个问题,以防有人有更好的解决方案
合并我的版本和@brianberns 提供的版本我觉得我最喜欢这个?
let fetchAsync3 (url: string) =
Http.AsyncRequestString(url)
|> Async.Catch
|> Async.RunSynchronously
let result = fetchAsync3 url
match result with
| Choice1Of2 html -> html
| Choice2Of2 error -> error.Message
a) 为什么会失败
你的 fetchAsync
函数 returns 和 Async<Choice<_>>
和你的模式匹配就好像函数只有 returns Choice<_>
。不幸的是,您不能在 Async 上进行模式匹配,因为那将是一个阻塞操作,正是 Async 尝试远离的操作。
b) 处理这些事情的惯用方式。
但是,您可以做的是留在异步上下文中并处理其中的故障。 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 包含在 not yet 中。但是你可以像这样自己定义它:
namespace global
[<RequireQualifiedAccess>]
module Async =
let map f xA =
async {
let! x = xA
return f x
}
或者,您可以使用提供语法糖的 F# computation expressions 以更加命令式的方式编写上述内容:
let fetchAsyncCe (url: string) =
async {
try
return!
FSharp.Data.Http.AsyncRequestString(url)
|> Async.map Ok
with
| e -> return Error e.Message
}
在这两个解决方案中,我都将异常转换为 F# 的 result 类型,我个人认为这是处理错误的最佳方式。
最后,如 brianberns 所示,您的表达式仅包含在 Async
类型中。但是 unlike C# 的 Task,async computations 表示程序如何计算一些东西,但是那个程序还没有启动,你必须明确地 运行 异步操作。其中一种方法是使用 Async.RunSynchronously:
Async.RunSynchronously (fetchAsyncCe "https://fsharpforfunandprofit.com/")
PS:您可能遇到过 Scott Wlaschin 的优秀 F# for fun and profit,但如果没有,您应该检查一下。就个人而言,我发现它是教您 F# 和一般函数式编程的最佳资源。
我一直在尝试找出惯用的 F# 方式来异步下载网页并处理任何 errors/HTTP 故障代码,我认为这是迄今为止我最接近的尝试,但我遇到了类型错误在 Choice1Of2 行
我愿意
a) 理解失败的原因(我还在学习 F#)
b) 知道这是否是正确的 approach/how 来完成这项工作,或者我是否完全走错了路
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")
无法编译的原因是因为您实际上从未 运行 fetchAsync
创建的计算。像这样的东西应该可以代替:
let fetchAsync url =
Http.AsyncRequestString(url)
|> Async.Catch
|> Async.RunSynchronously
再过几个小时喝了很多咖啡后,我有了这个
let fetchAsync2 (url: string) =
async { return! Async.Catch(Http.AsyncRequestString(url)) }
|> Async.RunSynchronously
let result = fetchAsync2 url
match result with
| Choice1Of2 html -> html
| Choice2Of2 error -> error.Message
这段代码按照我想要的方式工作,但我会留下这个问题,以防有人有更好的解决方案
合并我的版本和@brianberns 提供的版本我觉得我最喜欢这个?
let fetchAsync3 (url: string) =
Http.AsyncRequestString(url)
|> Async.Catch
|> Async.RunSynchronously
let result = fetchAsync3 url
match result with
| Choice1Of2 html -> html
| Choice2Of2 error -> error.Message
a) 为什么会失败
你的 fetchAsync
函数 returns 和 Async<Choice<_>>
和你的模式匹配就好像函数只有 returns Choice<_>
。不幸的是,您不能在 Async 上进行模式匹配,因为那将是一个阻塞操作,正是 Async 尝试远离的操作。
b) 处理这些事情的惯用方式。
但是,您可以做的是留在异步上下文中并处理其中的故障。 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 包含在 not yet 中。但是你可以像这样自己定义它:
namespace global
[<RequireQualifiedAccess>]
module Async =
let map f xA =
async {
let! x = xA
return f x
}
或者,您可以使用提供语法糖的 F# computation expressions 以更加命令式的方式编写上述内容:
let fetchAsyncCe (url: string) =
async {
try
return!
FSharp.Data.Http.AsyncRequestString(url)
|> Async.map Ok
with
| e -> return Error e.Message
}
在这两个解决方案中,我都将异常转换为 F# 的 result 类型,我个人认为这是处理错误的最佳方式。
最后,如 brianberns 所示,您的表达式仅包含在 Async
类型中。但是 unlike C# 的 Task,async computations 表示程序如何计算一些东西,但是那个程序还没有启动,你必须明确地 运行 异步操作。其中一种方法是使用 Async.RunSynchronously:
Async.RunSynchronously (fetchAsyncCe "https://fsharpforfunandprofit.com/")
PS:您可能遇到过 Scott Wlaschin 的优秀 F# for fun and profit,但如果没有,您应该检查一下。就个人而言,我发现它是教您 F# 和一般函数式编程的最佳资源。