chaining async rest calls in a pipeline while managing errors

谁都会走 提交于 2019-12-11 06:11:33

问题


Building on previous question based on synchronous calls, how do we approach asynchronous methodology in the following scenario.

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"

Test

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

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

Attempt

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"
 }

Error

This expression was epxected to have type 'Result' but here has type 'Async<'a>'


回答1:


You should explain what exception handling mechanism are you actually trying to implement here. For many things, using just normal F# built-in exceptions will work perfectly fine.

You are using Result.bind which is a way to implement an exception handling behaviour where, if any step throws an exception, the overall computation will fail. Is this what you want to achieve? If so, you can just rely on built-in F# async support for exception handling and have try .. with in the async computation that calls the two fetch functions. A minimal example:

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 }

If you want to call all fetch functions and fail only if all of them fail, then you will need to wrap individual fetch functions with exception handlers (as you do) and then pick the first result. Using normal F# option type, this can be done using 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 }

If you are starting with F#, then it is a lot easier to get started using things like core async functions and options - once you are comfortable with those, it makes sense to look at more sophisticated methods.



来源:https://stackoverflow.com/questions/49370853/chaining-async-rest-calls-in-a-pipeline-while-managing-errors

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!