Pass Func<Task> as parameter - c#

I have a method:
public async Task Method(Func<Task> action)
This function basically checks if few seconds have passed since the last run time.
I tried:
Func<Task> timer = async () => { await Task.Delay(3000);};
and invoking it like that
Func<Task> check = await _className.Method(timer);
However it says
Cannot implicitly convert 'void' to 'System.Func<System.Threading.Tasks.Task>'
Basically I am not able to pass a delayed task to a function.

Here's a trivial example of calling Method with a Func<Task>.
Func<Task> timer = async () => { await Task.Delay(3000);};
await Method(timer);
async Task Method(Func<Task> action)
{
await action();
}

I'm not sure exactly what you're trying to do, but this code works:
public class Program
{
public static async Task Main(string[] args)
{
var sw = Stopwatch.StartNew();
Func<Task> timer = async () => { await Task.Delay(3000); };
var theTask = Method(timer);
await theTask;
Console.WriteLine(sw.ElapsedMilliseconds); // ~3000
}
public static async Task Method(Func<Task> action)
{
await action();
}
}
I just put in var theTask = Method(timer); to illustrate that you can assign the return value to a Task before awaiting it.

Okay, so it works like tymtam already said - it looks like my problem was trying to assign end result to Func, i guess i need more sleep.
Thank you for the help tho
EDIT
I needed this:
Func<Task> check = async () => await _className.Method(timer);

Related

async/await is behaving weirdly by restarting already executed code

I am seeing some weird behavior related to async/await, but I can't explain it.
Here is the basic logic:
public async Task Init(UI.INavigable NavigateFrom) <-- Starts from here
{
await NavigateFrom.Navigate<UI.Pages.Page>(await Common.CreateDrillInNavigationTransitionInfo(), async (Page) =>
{
// ...
m_SelectPage.OnCategorySelected += OnCategorySelected;
// ... (including more awaits)
await OnModelUpdated();
return Page;
});
}
private async Task OnModelUpdated()
{
await m_SelectPage.DataUpdated(...);
}
public async Task DataUpdated(...)
{
try
{
m_Initializing = true;
await Common.RunUiTask(async () =>
{
// ... (including more awaits)
await Task.Run(async () =>
{
// ... (including more awaits)
System.Diagnostics.Debug.Print("DataUpdated: Calling CategorySelected\n");
await OnCategorySelected(await GetSelectedCategory());
});
});
}
finally
{
m_Initializing = false;
}
}
private async Task OnCategorySelected(Model.Category Category)
{
Debug.Print("OnCategorySelected start\n");
var Adaptor = new Model.Adaptor(...);
var p = new InfoPage(m_ActualApp, Adaptor);
Debug.Print("OnCategorySelected navigate\n");
await p.Init(SelectPage);
Debug.Print("OnCategorySelected end\n");
}
public async Task Init(UI.INavigable NavigateFrom)
{
Debug.Print("Init start\n");
await NavigateFrom.Navigate<UI.Pages.InfoPage>(await Common.CreateDrillInNavigationTransitionInfo(), async (Page) =>
{
Debug.Print("Init lambda\n");
// ...
Debug.Print("Init lambda update\n");
await m_InfoPage.Updated(...);
Debug.Print("Init lambda end\n");
return Page;
});
Debug.Print("Init end\n");
}
And here is the output from one run (it doesn't behave the same way every time):
DataUpdated: Calling CategorySelected
OnCategorySelected start
OnCategorySelected navigate
Init start
OnCategorySelected start
OnCategorySelected navigate
Init start
Init lambda
Init lambda update
OnCategorySelected start
OnCategorySelected navigate
Init start
Init lambda end
Init end
OnCategorySelected end
Init lambda
Init lambda update
Init lambda
Init lambda update
Init lambda end
Init end
OnCategorySelected end
Init lambda end
Init end
OnCategorySelected end
As is clearly seen from the output, it seems that as soon as it hits await in the Init function, or it hits await inside the lambda in the Init function, it suspends (which is correct), but then restarts back in the OnCategorySelected function for no apparent reason.
I can't explain what's happening. It should just happen once since there is no code that calls OnCategorySelected again. Any ideas?
For reference, here are the utility functions that I use in case there's some weirdness in those:
public static async Task<DrillInNavigationTransitionInfo> CreateDrillInNavigationTransitionInfo()
{
return await Common.RunUiTask(async () =>
{
return await Task.FromResult(new DrillInNavigationTransitionInfo());
});
}
public static class Common
{
public static async Task<T> RunUiTask<T>(Func<Task<T>> F)
{
return await CoreApplication.MainView.CoreWindow.Dispatcher.RunTaskAsync(async () =>
{
return await F();
});
}
}
public static class DispatcherTaskExtensions
{
public static async Task<T> RunTaskAsync<T>(this CoreDispatcher Dispatcher, CoreDispatcherPriority Priority, Func<Task<T>> Func)
{
var TaskCompletionSource = new TaskCompletionSource<T>();
await Dispatcher.RunAsync(Priority, async () =>
{
try
{
var R = await Func();
TaskCompletionSource.SetResult(R);
}
catch (Exception ex)
{
TaskCompletionSource.SetException(ex);
}
});
return await TaskCompletionSource.Task;
}
public static async Task<T> RunTaskAsync<T>(this CoreDispatcher Dispatcher, Func<Task<T>> Func)
{
return await RunTaskAsync(Dispatcher, CoreDispatcherPriority.Normal, Func);
}
}
As i see, everything is OK. Async means asynchronous, if u don't know. You invoked OnCategorySelected 3 times and it finished 3 times (you can see that in ur log). If you need async but sometimes synchronous completion is necessary you can use SemaphoreSlim for that.
SemaphoreSlim sl = new SemaphoreSlim(1, 1);
//some async code
await sl.WaitAsync();
//some sync code
sl.Release();
//again async code

Defer starting of a Task<T>

I have a series of methods (with variable number of parameters) that return a Task
I want to create a method that does something before and after each of this methods, by passing the Task.
I've simplified everything (removed cancellationToken, actual processing, etc) in this sample:
public async Task<string> GetDataString()
{
Console.WriteLine("Executing");
return "test";
}
public async Task<T> Process<T>(Task<T> task)
{
Console.WriteLine("Before");
var res = await task;
Console.WriteLine("After");
return res;
}
And in my main:
Task<string> task = GetDataString();
string result = await Process<string>(tasks);
Console.WriteLine(res);
the console output is
Executing
Before
After
test
What can I do to create the task but not actually starting it? And starting it only before the wait?
I managed to do it by creating a PauseToken, as explained in this article:
https://devblogs.microsoft.com/pfxteam/cooperatively-pausing-async-methods/
but I wonder if is there a better way.
Thanks,
Mattia
Your generic ProcessAsync method could accept a task factory as argument:
public async Task<T> ProcessAsync<T>(Func<Task<T>> taskFactory)
{
Console.WriteLine("Before");
var res = await taskFactory();
Console.WriteLine("After");
return res;
}
This way the task will be created at the time you'll invoke the factory method. You are in control of its creation.
Here is an example of calling the ProcessAsync method, passing as factory a lambda:
var result = await ProcessAsync(() => GetDataStringAsync(arg1, arg2));
This way you are not restricted to a factory method without arguments.
For completeness I should mention that Task objects can also created in a cold state using the constructor new Task(), and started later using the Start method, but this approach is not recommended.
You can remove the async keyword (from GetDataString) and create a new task which will be executed when you await
so the result of the code below is : before , executing , test , after
private static async Task Start()
{
Task<string> task = GetDataString();
string result = await Process<string>(task);
Console.WriteLine(result);
Console.ReadLine();
}
public Task<string> GetDataString()
{
return new TaskFactory(TaskScheduler.Default).StartNew(() =>
{
Console.WriteLine("Executing");
return "test";
});
}
public async Task<T> Process<T>(Task<T> task)
{
Console.WriteLine("Before");
var res = await task;
Console.WriteLine("After");
return res;
}

Async func is not awaiting

I am writing a wrapper function that executes an arbitrary number of async tasks and will provide retry and error handling policy. I'm having an issue awaiting the result of the async tasks.
The method call looks like this:
Execute(async () => await someAsyncFunction(someValue), async () await someOtherFunction(someValue))
My method implementation looks like this:
void Execute<T1, T2>(Func<T1> fn1, Func<T2> fn2, ... /* overloads for up to 6 functions */)
{
fn1();
fn2();
/* ... */
}
I've not yet applied the error handling and retry policy, but from debugging I've noticed that stepping over fn1 or fn2 is immediate, even when I put a large delay in, for example:
async Task someAsyncFunction(object value)
{
await Task.Delay(10000);
//...
}
Is it possible to achieve what I want with async methods?
An async "action" is actually a function that returns a Task, so it'll be a Func<Task>. You can create a collection of tasks and then await them all with Task.WhenAll. You can supply a flexible number of arguments using the params keyword.
Note also that Execute() must itself be async in order to make async calls.
public class Program
{
static async Task Execute(params Func<Task>[] actions)
{
var tasks = actions.Select( action => action() );
await Task.WhenAll(tasks);
}
public static async Task MainAsync()
{
await Execute
(
async () =>
{
await Task.Delay(3000);
Console.WriteLine("Function 1 done");
}
,
async () =>
{
await Task.Delay(3000);
Console.WriteLine("Function 2 done");
}
);
}
public static void Main()
{
MainAsync().GetAwaiter().GetResult();
}
}
Output:
Function 2 done
Function 1 done
Example code on DotNetFiddle

Why does this code ignore the await and proceed anyway?

I have the following code:
public Index () {
InitializeIndexAsync();
}
async Task InitializeIndexAsync () {
State = IndexState.Initializing;
await Task.Factory.StartNew(async () => {
// Initialize other things.
await IndexAsync();
});
State = IndexState.Ready;
}
I would expect that "State = IndexState.Ready" would not be hit until the asynchronous lambda completes, but debugging shows that line is hit long before the thread started above it completes. Why is this?
StartNew does not understand async lambdas, so when you pass it an async lambda, it will return a Task<Task>. Conceptually, the "outer" task only represents the start of the async lambda; the "inner" task represents the completion of the async lambda.
This is one of the reasons that StartNew is the wrong choice for async code, as I explain on my blog. A better solution is to use Task.Run, which was designed with async in mind:
async Task InitializeIndexAsync () {
State = IndexState.Initializing;
await Task.Run(async () => {
// Initialize other things.
await IndexAsync();
});
State = IndexState.Ready;
}
Not sure what you are trying to achieve by all these awaits...
I would try to keep it simple by having a synchronously method which initializes things, and then another MethodAsync which returns a Task and I can await on that task:
public async void Index()
{
await InitializeIndexAsync();
}
private Task InitializeIndexAsync()
{
return Task.Factory.StartNew(() => InitializeIndex());
}
private void InitializeIndex()
{
State = IndexState.Initializing;
// Initialize other things synchronously.
IndexAsync().Wait();
State = IndexState.Ready;
}
I hope this is what you meant.

How do i pass in a delegate as a parameter

I want to pass in a void or an int/string/bool(Which returns a value) Dynamically like so.
Delay(MyVoid);//I wont to execute a delay here, after the delay it will execute the the param/void like so...
public static void MyVoid()
{
MessageBox.Show("The void has started!");
}
public async Task MyAsyncMethod(void V)
{
await Task.Delay(2000);
V()
}
ps, I have tried using Delegates but it doesn't let be use it as a parameter.
Use an Action delegate to execute a method which returns void:
public async Task MyAsyncMethod(Action V)
{
await Task.Delay(2000);
V();
}
Or Func<T> for a method which returns some value
public async Task MyAsyncMethod(Func<int> V)
{
await Task.Delay(2000);
int result = V();
}

Categories