1

Considering the following

type MyClass () = 
    member x.ReadStreamAsync(stream:Stream) =
        async {
            let tcs = new TaskCompletionSource<int>()
            let buffer = Array.create 2048 0uy
            let! bytesReadCount = stream.ReadAsync(buffer, 0, buffer.Length) |> Async.AwaitTask
            if bytesReadCount > 0 then
                for i in 0..bytesReadCount do
                    if buffer.[i] = 10uy then
                        tcs.SetResult(i)

            // Omitted more code to handle the case if 10uy is not found..

            return tcs.Task
        }

The code reads from a stream until in meets a certain character (represented by a byte value) at which point the task returned by the method completes.

The function signature of DoSomethingAsync is unit -> Async<Task<int>>, but I would like it to be unit -> Task<int> such that it can be used more generally in .NET.

Can this be done in F# using an asynchronous expression, or do I can to rely more on the Task constructs of .NET?

kasperhj
  • 10,052
  • 21
  • 63
  • 106

1 Answers1

6

Given that you don't actually use the async workflow for anything in your example, the easiest solution would be to forgo it entirely:

member x.DoSomethingAsync() =
    let tcs = new TaskCompletionSource<int>()
    Task.Delay(100).Wait()
    tcs.SetResult(10)
    tcs.Task

This implementation of DoSomethingAsync has the type unit -> Task<int>.


It's not clear to me exactly what you're trying to do, but why don't you just do the following?

member x.DoSomethingAsync() =
    async {
        do! Async.Sleep 100
        return 10 } |> Async.StartAsTask

This implementation also has the type unit -> Task<int>.


Based on the updated question, here's a way to do it:

member x.DoSomethingAsync(stream:Stream) =
    async {
        let buffer = Array.create 2048 0uy
        let! bytesReadCount =
            stream.ReadAsync(buffer, 0, buffer.Length) |> Async.AwaitTask
        if bytesReadCount > 0
        then
            let res =
                [0..bytesReadCount]
                |> List.tryFind (fun i -> buffer.[i] = 10uy)
            return defaultArg res -1
        else return -1
    }
    |> Async.StartAsTask

The DoSomethingAsync function has the type Stream -> System.Task<int>. I didn't know what to do in the else case, so I just put -1, but I'm sure you can replace it with something more correct.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Suppose that instead of `Task.Delay` we return something that is required for the `SetResult` (delay was used for brevity in the example). Wouldn't we have to wait for that result using `let!` and hence require the computation expression? – kasperhj Jun 15 '15 at 11:20
  • Maybe the example is poorly constructed. I have attempted to update it to hopefully convey the intention more clearly. – kasperhj Jun 15 '15 at 11:43
  • @ploeh If `buffer` does not yet contain the delimiter, then instead of the task completing with a -1, then function should call itself again to read more of the stream. When a delimiter eventually arrives in the stream the task can finally complete. This is the behavior I was attempting to model with TaskCompletionSource. I can appreciate that `StartAsTask` makes this easier as I suppose the `else` block can call `DoSomethingAsync` again. – kasperhj Jun 15 '15 at 13:19
  • @lejon Sounds reasonable, although you'll probably need a recursive function for that. – Mark Seemann Jun 15 '15 at 13:25
  • @ploeh Thanks for the inputs and answers. – kasperhj Jun 15 '15 at 14:47