1

如何使用 F# 对公开基于事件的异步模式(例如WebClient类)的异步操作类的内置支持?

let Download(url : Uri) =
    let client = new WebClient()
    let html = client.DownloadString(url)
    html

当我尝试将其更改为使用“让!” 在异步块中(如 Soma 最近的帖子中所述)

let Download(url : Uri) =
    async {
    let client = new WebClient()
    let! html = client.DownloadStringAsync(url)
    return html }

我收到一条错误消息:

类型约束不匹配。单元类型与 Async<'a> 类型不兼容 'unit' 类型与'Async<'a>' 类型不兼容

编辑:我真的在问使用 *Async() 方法的一般问题,WebClient 只是一个简单的例子。微软“......你应该尽可能使用基于事件的异步模式[而不是 BeginFoo()/EndFoo()] 来公开异步功能......”所以我认为应该有一种简单的方法来消费任意*来自 F# 的 Async() 方法。

4

1 回答 1

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,它将为该方法返回一个异步包装器。

有时对象不使用这种模式,比如 WebClient,实际上你必须使用它Async.AwaitEvent来等待事件被触发,或者编写自己的循环来反复检查是否设置了布尔值。这是一篇关于将事件转换为异步对象的好文章。

对于它的价值,如果您安装了 F#,您还应该拥有源代码,这将使您了解 F# 团队如何实现他们的异步扩展。在我的机器上,相关文件位于:

C:\Program Files\FSharp-1.9.6.16\source\fsppack\FSharp.PowerPack\AsyncOperations.fs
于 2009-10-12T16:27:37.303 回答