Suppose that I have the following code:
namespace Library1
open System.Threading.Tasks
open System.Threading
open System.Runtime.Remoting.Messaging
open System
type public Class1() =
let printThread (message) =
printfn "%s %A" message Thread.CurrentThread.ManagedThreadId
let bar =
printThread ("first bar")
async {
printThread ("first async")
do! Async.Sleep(1000)
printThread "last async"
}
member this.X() = bar
I would like to use this class and invoke X from C#. The problem is X returns an Async<'T>. However, it is bad practice to expose F# specific types. So best practice is to return a Task. However Async.StartAsTask is kind of problematic since it will cause the code run in a seperate thread. What I want is I want to return a Task but also it should behave as Async.StartImmediate. Thus the non async part of the code should run in the original main thread. Here I assume I run it from UI thread so that all calls will return the same thread ID. In other words I want a Async.StartImmediate but returning a task. Is this achieavable ?
You can turn Async<'T>
into Task<'T>
using the Async.StartAsTask<'T>
method.
I'd generally recommend making things easy for the C# users and extend the F# implementation with an additional method that returns Task<'T>
. Following the usual naming convention, you can call the F# version AsyncFoo
and the C#-friendly version FooAsync
.
Looking at your example, I'd go with something like this:
type public Class1() =
let printThread (message) =
printfn "%s %A" message Thread.CurrentThread.ManagedThreadId
let bar =
printThread ("first bar")
async {
printThread ("first async")
do! Async.Sleep(1000)
printThread "last async"
}
member this.AsyncFoo() = bar
/// Expose C#-friendly asynchronous method that returns Task
member this.FooAsync() = Async.StartAsTask(bar)
/// Expose C#-friendly asynchronous method that returns Task
/// and takes cancellation token to support cancellation...
member this.FooAsync(cancellationToken) =
Async.StartAsTask(bar, ?cancellationToken=cancellationToken)
This Works exactly as I want (unlike the question this version returns an int as well which is plus):
type public Class1() =
let printThread (message) = printfn "%s %A" message Thread.CurrentThread.ManagedThreadId
let bar =
printThread ("first bar")
async {
printThread ("first async")
do! Async.Sleep(1000)
printThread "last async"
return 1232
}
member this.convertToTask<'T> (asyn : Async<'T>) =
let tcs1 = new TaskCompletionSource<'T>()
let t1 = tcs1.Task
Async.StartWithContinuations
(
asyn
, (fun (k) -> tcs1.SetResult(k)), (fun exn -> tcs1.SetException(exn)), fun exn -> ())
t1
member this.X() : Task<int> = (bar |> this.convertToTask)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With