I would like to translate the C# method DelayNe below to an F# function in such a way that it can be used from C# in exactly the same way. The reason is that I want to put it in an F# library. I am struggling a lot trying to google my way to an answer, with no luck obviously.
Update: Sorry for not being clear. I have tasks that call Task.Delay in the way that you see in the DelayNe method below. The problem is that when _cancellationTokenSource.Cancel() is called, then Task.Delay throws an exception, and I don't want that exception. In other words, I believe I need a wrapper around Task.Delay which will catch the exception and silence it, but otherwise expose the behavior of Task.Delay exactly as is. And I want to implement the wrapper in F#.
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
async Task DelayNe(int milliSeconds, CancellationToken token)
{
try
{
await Task.Delay(milliSeconds, token);
}
catch (TaskCanceledException ex)
{
("Silencing " + ex.GetType().Name + ", " + ex.Message).Dump();
}
}
async Task Test(CancellationToken cancellationToken)
{
try
{
"Test start".Dump();
await DelayNe(5000, cancellationToken);
"Test stop".Dump();
}
catch (Exception ex)
{
("Catch: " + ex.Message + ", " + ex.GetType().Name).Dump();
}
}
async Task Main()
{
"Start".Dump();
_cancellationTokenSource.CancelAfter(1000);
var testTask = Test(_cancellationTokenSource.Token);
await testTask;
"Stop".Dump();
}
1 Answer 1
F# asynchronous workflows have built-in support for cancellation, so using those instead of working with tasks will probably make your life a lot easier. Your question does not really provide enough information to understand what you are trying to do, but below is an example of using async for something that is very close to your example:
let delayNe ms = async {
use! _d = Async.OnCancel(fun () ->
printfn "cancelled!")
do! Async.Sleep(ms) }
let test = async {
printfn "Test start"
do! delayNe 5000
printfn "Test stop" }
let DoWork () =
let cts = new System.Threading.CancellationTokenSource()
cts.CancelAfter(1000)
Async.StartAsTask(test, cancellationToken = cts.Token)
The DoWork function starts the work as a task, which can be easily consumed from C#.
4 Comments
await Task.Delay(ms, token); so that I can abort the delay, but without having it throw the exception. So I want a wrapper for that, but implemented in F#. Thanks, I am trying to use this now.Async.Sleep works well with the F# cancellation mechanism - it will not throw an exception and you can handle cancellation using Async.OnCancel. I suspect you are doing all this for some reason - so if you can share the reason, you might get a better advice.
async voidshould only be used for event handlers because it can't be awaited. Furthermore, cold tasks are never needed. Don't use them in C#await Test(cts.Token);? Or store the return value otTest, ievar testTask = Test(_cancellationTokenSource.Token)? It's already a Task.asyncdoesn't mean you need to start a task. It means that you await for an asynchronous operation like IO or calling a web service to complete, without blocking the current threadasync/await. Things won't be easier if you switch to F#asyncblock