如何使用F#内置支持异常操作类公开Event-based Asynchronous Pattern,例如 WebClient 类?
let Download(url : Uri) =
let client = new WebClient()
let html = client.DownloadString(url)
html
当我尝试将其更改为使用“let!”时在 async 块中(比如Soma最近的post中所述)
let Download(url : Uri) =
async {
let client = new WebClient()
let! html = client.DownloadStringAsync(url)
return html }
我收到错误消息:
类型约束不匹配。类型单元与Async<'a>类型不兼容。类型“单位”与“Async<”a>'
类型不兼容
编辑:我真的在询问使用* Async()方法的一般问题,WebClient只是一个简单的例子。 Microsoft says“......你应该尽可能使用基于事件的异步模式[而不是BeginFoo()/ EndFoo()]公开异步功能......”所以我认为应该有一个简单的方法从F#中使用任意* Async()方法。
答案 0 :(得分:5)
WebClient.DownloadStringAsync方法是.NET框架的一部分。它会引发一个事件来表示它的进度,它的返回类型是unit
,所以你不想使用它,将它包装在async
对象中是没有优势的。
F#PowerPack定义了一种扩展方法val webclient.AsyncDownloadString : uri -> Async{string}
:
let Download(url : Uri) =
async {
let client = new WebClient()
client.Encoding <- Encoding.GetEncoding("utf-8")
let! html = client.AsyncDownloadString(url)
return html }
不幸的是,名称的选择与现有的webclient方法发生冲突,这可以理解地引起混淆。但是,我相信所有F#异步扩展都以Async*
开头。
[编辑添加以回复评论:]
通常,.NET使用BeginFoo / EndFoo模式进行并发。如果类型正确,您可以使用Async.BuildPrimitive beginMethod endMethod
,它将返回该方法的Async包装器。
有时候对象不使用这种模式,比如WebClient,你实际上必须使用Async.AwaitEvent
等待事件被触发,或者编写你自己的循环来反复检查bool是否是组。这是一个不错的article on converting events to Async objects。
为了它的价值,如果您安装了F#,您还应该拥有源代码,它将让您了解F#团队如何实现其异步扩展。在我的机器上,相关文件位于:
C:\Program Files\FSharp-1.9.6.16\source\fsppack\FSharp.PowerPack\AsyncOperations.fs