How does the control flow stay in scope of the async method after it has returned the result? Is this similar to variable closure?
In the code below, 42 is printed to the Console before Almost Done. Since 42 is already returned, how is the control still in AsyncCheck
? I understand that if the task did not complete with "pending" it will return the control, but here I have an explicit return statement, so I am having problems with that.
async static Task<int> AsyncCheck()
{
await Task.Factory.StartNew( async () =>
{
Console.WriteLine("Start awesome work");
await Task.Delay(1000);
Console.WriteLine("Almost done");
await Task.Delay(1000);
Console.WriteLine("Ok, done!");
});
return 42;
}
async static void AsyncCall()
{
int result = await AsyncCheck();
Console.WriteLine(result);
}
I am using "async void" just to test this sample.
source to share
You don't have to use Task.Factory.StartNew
with async-await
. You must use Task.Run
. Task.Factory.StartNew
came in .Net 4.0 without async-await
. You can still use it, but if you don't have a good reason, there Task.Run
is a safer alternative.
Instead of returning a, Task
it returns a Task<Task>
, which needs to be expanded before waiting:
Task<Task> task = Task.Factory.StartNew( async () =>
{
Console.WriteLine("Start awesome work");
await Task.Delay(1000);
Console.WriteLine("Almost done");
await Task.Delay(1000);
Console.WriteLine("Ok, done!");
});
Task unwrappedTask = task.Unwrap();
await unwrappedTask
Without it, you can't wait for you to finish. Everything you do uses a thread ThreadPool
to run this asynchronous method.
Basically you are executing your lambda in parallel on a different thread while the code flow continues. This is why you see "42" to "Almost done"
source to share
Let desagar! You
async static Task<int> AsyncCheck()
{
await Task.Factory.StartNew( async () =>
{
Console.WriteLine("Start awesome work");
await Task.Delay(1000);
Console.WriteLine("Almost done");
await Task.Delay(1000);
Console.WriteLine("Ok, done!");
});
return 42;
}
async static void AsyncCall()
{
int result = await AsyncCheck();
Console.WriteLine(result);
}
The compiler includes the following:
static Task<int> AsyncCheck()
{
Task t = Task.Factory.StartNew( () =>
{
Console.WriteLine("Start awesome work");
Task d1 = Task.Delay(1000);
d1.ContinueWith(t1 => {
Console.WriteLine("Almost done");
Task t2 = Task.Delay(1000);
t2.ContinueWith( tt2 => {
Console.WriteLine("Ok, done!");
}
}
});
//no dependecies on t, so just return straight away
return Task.FromResult(42);
}
static void AsyncCall()
{
Task<int> result = AsyncCheck();
result.ContinueWith( t => {
Console.WriteLine(t.Result);
})
}
ContinueWith
will run its body upon completion of the task. This should make the control flow transparent.
source to share