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.