【问题标题】:chaining async rest calls in a pipeline while managing errors在管理错误时在管道中链接异步休息调用
【发布时间】:2018-03-19 19:25:07
【问题描述】:

在之前基于同步调用的question 的基础上,我们如何在以下场景中处理异步方法。

let fetch1 (result: string) : Result<string, string> =
    try
        use request = WebRequest.Create("http://bing.com") :?> HttpWebRequest
        use response = request.GetResponse()
        use reader = new StreamReader(response.GetResponseStream())
        let html = reader.ReadToEnd()
        Ok "success"
    with
        | :? WebException as e ->
        Error "error with the url"

let fetch2 (result: string) : Result<string, string> =
    try
        use request = WebRequest.Create("http://google.com") :?> HttpWebRequest
        use response = request.GetResponse()
        use reader = new StreamReader(response.GetResponseStream())
        let html = reader.ReadToEnd()
        Ok "success"
    with
        | :? WebException as e ->
        Error "error with the url"

let fetch3 (result: string) : Result<string, string> =
     try
         use request = WebRequest.Create("http://invalid.com") :?> HttpWebRequest
         use response = request.GetResponse()
         use reader = new StreamReader(response.GetResponseStream())
         let html = reader.ReadToEnd()
         Ok "success"
     with
         | :? WebException as e ->
         Error "error with the url"

测试

let chain = fetch1 >> Result.bind fetch2 >> Result.bind fetch3

match chain("") with 
| Ok message -> Debug.WriteLine(message)
| Error error -> Debug.WriteLine(error)

尝试

let fetch1 (result: string) :Result<string, string> = async {
    try
        use! request = WebRequest.Create("http://bing.com") :?> HttpWebRequest
        use response = request.GetResponse()
        use reader = new StreamReader(response.GetResponseStream())
        let html = reader.ReadToEnd()
        Ok "success"
    with
        | :? WebException as e ->
        Error "error with the url"
 }

错误

此表达式的类型为“Result”,但此处的类型为“Async'

【问题讨论】:

  • 好吧,您将表达式更改为async,但您仍然坚持其类型应为Result&lt;string, string&gt;。难怪编译器会感到困惑。你有什么不清楚的地方?
  • 返回类型应该是什么,以及代码中的任何修复,我相信类似use! request,感谢答案中的代码帮助。谢谢
  • async 计算属于Async&lt;_&gt; 类型,因为编译器会很有帮助地告诉您。您需要决定应该在 Async&lt;_&gt; 中包含什么类型 - 即您的异步计算应该产生什么类型。
  • 对不起,我没有你流利,我是初学者,有完整的代码图就可以理解了。
  • 你的意思是像这样Async&lt;Result&lt;Article, Error&gt;&gt;?我现在在正文中遇到其他错误,这就是为什么我建议一个完整的代码示例

标签: asynchronous f# function-composition


【解决方案1】:

您应该在此处说明您实际尝试实现的异常处理机制。对于很多事情,只使用普通的 F# 内置异常就可以很好地工作。

您正在使用Result.bind,这是一种实现异常处理行为的方法,如果任何步骤抛出异常,整体计算将失败。这是你想要达到的目标吗?如果是这样,您可以只依靠内置的 F# 异步支持来处理异常,并在调用两个 fetch 函数的异步计算中使用 try .. with。一个最小的例子:

let fetch url = async {
  use wc = new WebClient()
  let! res = wc.AsyncDownloadString(System.Uri(url))
  return res }

let fetchAllOrNothing = async {
  try 
    let! goog = fetch "http://www.google.com"
    let! bing = fetch "http://www.bing.com"
    return Some(goog.Length, bing.Length)
  with _ ->
    return None }

如果您想调用所有 fetch 函数并且仅在所有函数都失败时才失败,那么您需要使用异常处理程序(如您所做的那样)包装各个 fetch 函数,然后选择第一个结果。使用普通的 F# 选项类型,这可以使用 Array.tryPick 来完成:

let fetch url = async {
  try
    use wc = new WebClient()
    let! res = wc.AsyncDownloadString(System.Uri(url))
    return Some res 
  with _ -> 
    return None }

let fetchFirstOne = async {
  let! all = 
    [ fetch "http://www.google.com"
      fetch "http://www.bing.com" ] |> Async.Parallel
  return Array.tryPick (fun x -> x) all }

如果您从 F# 开始,那么开始使用诸如核心 async 函数和选项之类的东西会容易得多 - 一旦您对这些感到满意,就可以考虑使用更复杂的方法。

【讨论】:

  • 对不起,我在这里搞糊涂了,获取 url 是一个例子,我的实际工作有很多不同的东西,而且必须是不同的个人功能,你能不能将我的示例与 3 个函数调用 fetch1、fetch2 和 fetch3 一起使用,我无法在一个函数调用中概括所有内容。否则我必须改变问题并摆脱获取网址并提出一个不同的例子。
  • @Developer11 只需将我的示例中的 fetch "oneUrl"fetch "anotherUrl" 替换为您的 work1 ()work2 () 函数 - 其余部分保持不变。
  • 链发生了什么,链将如何工作,我应该从那里摆脱 Result.bind 吗?我还想查看带有 match 语句的链调用的签名。根据我的“测试”
  • 您不需要 Result.bind - 我的示例是自包含的 - 它向您展示了两种组合 async 计算的方法。
  • 好的,请给我看看 compose 链语句,然后我可以回去测试
猜你喜欢
  • 1970-01-01
  • 2023-03-29
  • 2016-11-08
  • 2019-04-12
  • 2015-05-07
  • 2019-11-23
  • 2017-07-28
  • 1970-01-01
  • 2016-02-18
相关资源
最近更新 更多