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.