i am creating a task scheduler so i am trying to make some kind of repeating function that accepts Task and awaits it but i get a strange Exception of Type 'T' is not awaitable
public static Task<T> Interval<T>(TimeSpan pollInterval, Func<T> action, CancellationToken token)
{
return Task.Factory.StartNew(
async () =>
{
for (; ; )
{
if (token.WaitCancellationRequested(pollInterval))
break;
await action();
}
}, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
So can anyone tell me how could i await a that generic Task cuz i want the function to accept any Task, Task, bool or any other type ?
You don't need to start a long running task for this - just make your method asynchronous directly:
public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Action action, CancellationToken token)
{
while(true)
{
await Task.Delay(pollInterval, token);
action();
}
}
This will cause the Action
to run on the current context. If that is not required, you can use:
await Task.Delay(pollInterval, token).ConfigureAwait(false);
action();
This will cause the Action
to not run on the same synchronization context of the caller, and potentially use a ThreadPool thread.
Edit in response to comments:
If you don't want the resulting task to come back canceled, but just return when the token is fired, you could use:
public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Action action, CancellationToken token)
{
while(!token.IsCancellationRequested)
{
try
{
await Task.Delay(pollInterval, token);
action();
}
catch(OperationCanceledException e)
{
// Swallow cancellation - dangerous if action() throws this, though....
break;
}
}
}
Edit 2:
If you want to pass in async
lambdas, you should make the method take an Func<Task>
, not Action
:
public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Func<Task> actionTask, CancellationToken token)
{
while(!token.IsCancellationRequested)
{
try
{
await Task.Delay(pollInterval, token);
}
catch(OperationCanceledException e)
{
// Swallow cancellation
break;
}
await actionTask();
}
}
Edit in response to chat:
If you want to poll, but use the results of an operation, you could use:
public static async Task RunAtIntervalAsync<T>(TimeSpan pollInterval, Func<Task<T>> fetchOperation, Action<T> operationOnResult, CancellationToken token)
{
while(!token.IsCancellationRequested)
{
try
{
await Task.Delay(pollInterval, token);
}
catch(OperationCanceledException e)
{
// Swallow cancellation
break;
}
// Get a value
T value = await fetchOperation();
// Use result (ie: update UI)
operationOnResult(value);
}
}
You could then call this via:
RunAtIntervalAsync(TimeSpan.FromSeconds(1),
async () => { await Task.Delay(1000); return "Foo"; },
result => UpdateUI(result),
token);
You can't.
You can make a function that takes a generic asynchronous function – a function that returns a Task<T>
.
That would be a Func<Task<T>>
.
You can also make a function that takes a generic synchronous function, which is what you have now.
You can't make a single function that can take either, but you can make two overloads.
On an unrelated note, your function never actually uses the return value of the function.
Therefore, you shouldn't make it generic at all; you should instead take a Func<Task>
or an Action
.
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