Reputation: 341
I'm fairly new to F# and as practice I'm currently writing a module that has a lot of interop with C# libraries.
When library code returns me Task
, it's not a problem - I already found a package that enables let!
with tasks.
Right now I'm using cliwrap and its ExecuteAsync()
method returns not a Task
, but some other awaitable type(you can await
it in C# and it has GetAwaiter()
method).
So far I came up with the following code to use it(please ignore usage of test
and ()
, its temprorary for debugging):
let testLaunchDelayed = async {
let cmd = Cli.Wrap(@"C:\Launcher\Launcher.exe").WithArguments("--TestName " + testName)
let test = cmd.ExecuteAsync().GetAwaiter().GetResult()
()
}
Async.StartImmediate testLaunchDelayed
It seems to work, but using .GetAwaiter().GetResult()
feels wrong. Is there a better way ?
Update
Tried using taskbuilder
, ended up with this:
async {
let cmd = Cli.Wrap(@"C:\Test\git\tests\func\nunit\AllBin\publish_win10-x64\CommonTestLauncher.exe").WithArguments("--TestName " + testName);
let! cmdTask = task{return! cmd.ExecuteAsync()}
()
}
Upvotes: 1
Views: 223
Reputation: 243051
The issue with using GetResult
is that this is a synchronous blocking call. Ideally, you want to use the OnCompleted
event to get notified when the operation completes and, only then, access the result. You can encapsulate this into a helper awaitAwaiter
, which is much like the Async.AwaitTask
operation:
let awaitAwaiter (a:Runtime.CompilerServices.TaskAwaiter<_>) =
Async.FromContinuations(fun (cont, econt, ccont) ->
a.OnCompleted(fun () ->
let res =
try Choice1Of3 (a.GetResult())
with
| :? System.Threading.Tasks.TaskCanceledException as ce -> Choice2Of3 ce
| e -> Choice3Of3 e
match res with
| Choice1Of3 res -> cont res
| Choice2Of3 ce -> ccont (OperationCanceledException("Task has been cancelled", ce))
| Choice3Of3 e -> econt e ) )
This uses FromContinuations
to create a new asynchronous computation that uses OnCompleted
to get notified when the work is done and then triggers appropriate continuation (handling errors - hopefully correctly, but I have not tested this!)
The use is the same as with AwaitTask
:
let testLaunchDelayed testName = async {
let cmd = Cli.Wrap(@"C:\Launcher\Launcher.exe").WithArguments("--TestName " + testName)
let test = cmd.ExecuteAsync().GetAwaiter() |> awaitAwaiter
() }
Upvotes: 3