问题
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