我正试图让F#异步工作,而我无法弄清楚我做错了什么。这是我运行的sorta同步代码:
open System.Net
open System.Runtime.Serialization
open System.Threading.Tasks
[<DataContract>]
type Person = {
[<field: DataMember(Name = "name")>]
Name : string
[<field: DataMember(Name = "phone")>]
Phone : int
}
let url = "http://localhost:5000/app/plugins/anon/CCure"
let js = Json.DataContractJsonSerializer(typeof<Person>)
let main x =
let client = new WebClient()
let url = url + "/" + x
let reader = client.OpenRead(url)
let person = js.ReadObject(reader) :?> Person
printfn "Name: %s, Phone number: %d" person.Name person.Phone
printfn "starting x"
let x = Task.Factory.StartNew(fun () -> main "x")
printfn "starting y"
let y = Task.Factory.StartNew(fun () -> main "y")
Task.WaitAll(x, y)
我当时认为要异步运行它会起作用,但事实并非如此:
open System.Net
open System.Runtime.Serialization
open System.Threading.Tasks
[<DataContract>]
type Person = {
[<field: DataMember(Name = "name")>]
Name : string
[<field: DataMember(Name = "phone")>]
Phone : int
}
let url = "http://localhost:5000/app/plugins/anon/CCure"
let js = Json.DataContractJsonSerializer(typeof<Person>)
let main x = async {
let client = new WebClient()
let url = url + "/" + x
let! reader = client.OpenReadAsync(url)
let person = js.ReadObject(reader) :?> Person
printfn "Name: %s, Phone number: %d" person.Name person.Phone }
printfn "starting x"
let x = Task.Factory.StartNew(fun () -> main "x")
printfn "starting y"
let y = Task.Factory.StartNew(fun () -> main "y")
Task.WaitAll(x, y)
$ fsharpc -r System.Runtime.Serialization foo.fs&amp;&amp; ./foo.exe F# 编译器为F#3.1(开源版)自由发布的 Apache 2.0开源许可证
/home/frew/code/foo.fs(19,18):错误FS0001:这个表达式是 预计有类型 异步&LT;'一&GT;但这里有类型 单位
/home/frew/code/foo.fs(20,17):错误FS0041:一个独特的重载 方法'ReadObject'无法根据类型信息确定 在此计划点之前。可能需要类型注释。 候选人:XmlObjectSerializer.ReadObject(读者: System.Xml.XmlDictionaryReader):obj, XmlObjectSerializer.ReadObject(reader:System.Xml.XmlReader):obj, XmlObjectSerializer.ReadObject(stream:System.IO.Stream):obj
/home/frew/code/foo.fs(20,17):错误FS0008:此运行时强制或 从类型进行类型测试 '到 人员涉及基于此计划点之前的信息的不确定类型。不允许运行时类型测试 一些类型。需要进一步的类型注释。
我在这里缺少什么?
答案 0 :(得分:2)
OpenReadAsync
是.NET BCL的一部分,因此在设计时不考虑F#async。您会注意到它返回unit
,而不是Async<Stream>
,因此它不会与let一起使用!。
API旨在与事件一起使用(即您必须连接client.OpenReadCompleted
)。
你有几个选择。
FSharp.Core
中有一些很好的辅助方法可以提供帮助
您将API转换为更友好的F#(请参阅
Async.AwaitEvent
)。AsyncDownloadString
,这是可以在Microsoft.FSharp.Control.WebExtensions
中找到的WebClient扩展方法。这比较容易,所以我在下面做了,虽然它确实意味着将整个流作为字符串保存在内存中,所以如果你有大量的Json,这可能不是最好的主意。使用async代替并行运行的任务也是更惯用的F#。
open System.Net
open System.Runtime.Serialization
open System.Threading.Tasks
open Microsoft.FSharp.Control.WebExtensions
open System.Runtime.Serialization.Json
[<DataContract>]
type Person = {
[<field: DataMember(Name = "name")>]
Name : string
[<field: DataMember(Name = "phone")>]
Phone : int
}
let url = "http://localhost:5000/app/plugins/anon/CCure"
let js = Json.DataContractJsonSerializer(typeof<Person>)
let main x = async {
printfn "Starting %s" x
let client = new WebClient()
let url = url + "/" + x
let! json = client.AsyncDownloadString(System.Uri(url))
let bytes = System.Text.Encoding.UTF8.GetBytes(json)
let st = new System.IO.MemoryStream(bytes)
let person = js.ReadObject(st) :?> Person
printfn "Name: %s, Phone number: %d" person.Name person.Phone }
let x = main "x"
let y = main "y"
[x;y] |> Async.Parallel |> Async.RunSynchronously |> ignore<unit[]>