You have hit upon the major technical constraints of async
methods. I would offer this additional guidance:
- It is pointless to declare an
async
method which does not await
at least one operation, as such a method is equivalent to a regular, synchronous method.
- You should avoid writing
async void
methods. These are intended only to support the writing of asynchronous event handlers.
- If you call an
async
method, you should await
it (or at least access the Task's IsFaulted
status and retrieve its Exception
if one occurred). If you do not, and one of the awaited operations inside that method throws an exception that you do not catch, that exception will be rethrown on the finalizer thread1 (bad).
You can await
any object with an awaitable type, i.e., a type which declares a member:
TAwaiter GetAwaiter();
Where TAwaiter
is a type declaring the following members:
bool IsCompleted { get; }
TResult GetResult();
void OnCompleted(Action continuation); // inherited from INotifyCompletion
The IsCompleted
property is self-describing: it returns true
if the awaitable operation has already completed; otherwise, it returns false.
The GetResult()
method returns the result of the operation, or returns void
if the operation has no return type. It should throw if called while IsCompleted
is false
.
The OnCompleted()
method is used to register a continuation, i.e., the code which is called upon completion of the operation. This member should actually be inherited from INotifyCompletion
in the System.Runtime.CompilerServices
namespace (all awaiters should implement this interface).
1This is no longer the default behavior in .NET 4.5, though it can still be enabled on the client machine through a configuration change.