static async Task WaitTaskCompleted()
{
//Use Thread A before await Task.CompletedTask
await Task.CompletedTask;
//Will the code after await Task.CompletedTask always use Thread A, or there is chance to have a Thread B?
}
which means await Task.CompletedTask
will always actually perform the method synchronously?
Yes, this code will always run synchronously; the main continuation compiler goo is only invoked when the first incomplete awaitable is encountered.
You can see this in sharplab - in particular, here:
awaiter = Task.CompletedTask.GetAwaiter();
if (!awaiter.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
return;
}
It is the AwaitUnsafeOnCompleted(...)
+ return
that implements asynchronicity.
Just for the sanity of other readers, the usual purpose of Task.FromResult<T>
, Task.CompletedTask
, Task.FromCancelation
and Task.FromException()
is to provide simple factory methods for various types of Task
(i.e. with / without return payload, or to return an exception or mimic cancellation), and in all cases, the tasks returned will be regarded as IsCompleted
, as per the source:
private const int TASK_STATE_COMPLETED_MASK = TASK_STATE_CANCELED | TASK_STATE_FAULTED
| TASK_STATE_RAN_TO_COMPLETION;
As per @Marc's answer, awaiting an already IsCompleted
Task short circuits the awaiter and execution will continue synchronously on the same thread.
As per my comment, it would be highly unusual to directly await
a Task created by Task.CompletedTask
or Task.FromResult
, as this compiler would generate an unnecessary async state machine wrapper, which is total overkill in the OP's scenario.
A common scenario for using the various completed Task factory methods would be in mocking during Unit testing, where the class / interface being mocked requires a Task
to be returned but otherwise has no need for async
. For example, if the following production interface needed mocking or stubbing:
public interface ISomeInterface
{
Task<DateTime> GetDateAsync();
}
which could be stubbed as follows:
public class MyMock : ISomeInterface
{
public Task<DateTime> GetDateAsync() // no async
{
// Directly return a completed task return a fake result
return Task.FromResult(new DateTime(2019, 11, 12, 0, 0, 0, DateTimeKind.Utc));
}
}
The production class being tested (SUT) would then likely await the result of GetDateAsync()
on the injected (and now mocked) ISomeInterface
, and will usually be none the wiser that the called method just rubber stamped the Task and returned fake data synchronously.
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