在管理错误时,在管道中链接异步休息

时间:2018-03-19 19:25:07

标签: asynchronous f# function-composition

基于同步调用构建在先前的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"
 }

错误

  
    

此表达式表示类型为&#39;结果&#39;但这里有类型&#39; Async&lt;&#39; a&gt;&#39;

  

1 个答案:

答案 0 :(得分:2)

您应该解释一下您实际尝试在此实现的异常处理机制。对于很多东西,使用普通的F#内置异常将完美地运行。

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

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函数和选项之类的东西要容易得多 - 一旦你对它们感到满意,看看更复杂的方法是有意义的。 / p>