I am experimenting a bit with F# and I wrote a class that listens incoming UDP packets, prints them, and continues to listen.
I have four different implementations that all accomplish this.
type UdpListener(endpoint:IPEndPoint) =
let client = new UdpClient(endpoint)
let endpoint = endpoint
let rec listenAsync1() =
async {
let! res = client.ReceiveAsync() |> Async.AwaitTask
res.Buffer |> printfn "%A"
return! listenAsync1()
}
let rec listenAsync2() =
async {
let res = client.Receive(ref endpoint)
res |> printfn "%A"
do! listenAsync2()
}
let rec listenAsync3() =
async {
let res = client.Receive(ref endpoint)
res |> printfn "%A"
listenAsync3() |> Async.Start
}
let rec listenAsync4() =
async {
while true do
let res = client.Receive(ref endpoint)
res |> printfn "%A"
}
member this.Start() =
listenAsync1() |> Async.Start
listenAsync1
attempts to leverage the awaitable returned by client.ReceiveAsync()
and re-listens using recursion. This approach feels most functional to me.
However, the async computation expression will actually run the code in the async
block on a TP thread, so is it really necessary to using the Task based client.ReceiveAsync()
?
listenAsync2
accomplishes the same result as listenAsync1
by using a blocking call on a TP thread.
listenAsync3
uses a slightly different way of recursively kicking off the listener again.
listenAsync4
uses a loop. It states intent pretty clearly, but is not really that pretty.
Is there an advantage of ever using Task based async in F#? It seems superfluous when wrapped inside an async computation expression which seems similar to Task.Run(..)
in C#.
Which of the methods (if any!) is generally accept as best practice, and why? (Perhaps they can be ranked?)