Asynchronous programming is difficult. Fortunately, C# has
all the wonders of the Task Parallel Library to help us out. However, there is
still a need to understand what properties we get with an async function
definition, or what kind of properties we get when awaiting a task versus
calling .Wait(). Knowing the properties will leave us better prepared to make
decisions around which method we wish to use. So I made a little async await
cheat sheet to help remember the properties of the different task scenarios.
Enjoy~
A pdf version to take along with you can be found here.
A pdf version to take along with you can be found here.
o    Task
Usage:
•        
Task<int> longTask = LongRunningCalculation()
— 
Task starts immediately.
— 
Runs to first await in LongRunningCalculation() on current
thread.
— 
Returned Task<int> can be passed around to be awaited
in the future.
•        
await longTask
— 
Does not block current thread.
— 
Unwraps Task<int> into int.
— 
Throws first exception that is triggered.
•        
longTask.Wait()
— 
Blocks current thread until longTask has completed.
— 
Throws AggregateException which needs to be unwrapped to find
triggered exception.
•        
longTask.Wait(Timespan.FromSeconds(5))
— 
Blocks current thread until longTask has completed or the
timeout has occurred.
— 
Throws AggregateException which needs to be unwrapped to find
triggered exception.
— 
Returns true when longTask ran to completion, and false when
longTask timed out.
— 
longTask may still be running even after the timeout.
•        
longTask.Result
— 
Blocks current thread until longTask has completed.
— 
Unwraps Task<int> into int.
— 
Throws AggregateException which needs to be unwrapped to find
triggered exception.
•        
longTask.GetAwaiter().GetResult()
— 
Blocks current thread until longTask has completed.
— 
Unwraps Task<int> into int.
— 
Throws first exception that is triggered.
o    Batch
Task Usage:
•        
Task<int>[] longTasks =
{ LongRunningCalculation(),
LongRunningCalculation(), LongRunningCalculation() } 
— 
All tasks start immediately.
— 
All tasks run to first await in each LongRunningCalculation()
on current thread.
•        
Task.WhenAll(longTasks)
— 
Batches up all of longTasks into a single awaitable task.
— 
Runs all of longTasks in parallel when scheduling permits.
— 
Returned Task<int[]> can be passed around to be awaited
in the future.
•        
await Task.WhenAll(longTasks)
— 
Does not block current thread.
— 
Unwraps Task<int[]> into int[].
— 
Finishes running all tasks before any exception is thrown.
— 
Throws first exception that is triggered.
•        
Task.WaitAll(longTasks)
— 
Blocks current thread until all longTasks have completed.
— 
Finishes running all tasks before any exception is thrown.
— 
Throws AggregateException which needs to be unwrapped to find
triggered exception.
•        
Task.WaitAll(longTasks, Timespan.FromSeconds(5))
— 
Blocks current thread until all longTasks have completed or
the timeout has occurred.
— 
Finishes running all tasks before exception is thrown.
— 
Throws AggregateException which needs to be unwrapped to find
triggered exception.
— 
Returns true when longTasks all ran to completion, and false
when longTasks timed out.
— 
longTasks may still be running even after the timeout.
o    Configuring
Task Await Options:
•        
await longTask.ConfigureAwait(false)
— 
Configures the task to not need to return to the synchronization context it
was originally invoked with. The context is usually the thread it was invoked on.
— 
Lowers chance of deadlocks and should be preferred.
•        
await longTask.ConfigureAwait(true)
— 
Configures the task to need to return to the synchronization context it was
originally invoked with. The context is usually the thread it was invoked on.
— 
This is the default for awaited tasks without calling
.ConfigureAwait().
— 
Usually used when modifying UI elements that need to occur on
the UI thread.
o    Function
Definitions:
•        
int Method()
— 
Method can internally contain Tasks.
— 
Method can internally .Wait() Tasks.
— 
Method cannot internally await Tasks.
— 
Method can return int.
— 
Method cannot be awaited on externally.
— 
Can externally catch exceptions thrown by Method.
•        
Task<int> Method()
— 
Method can internally contain Tasks.
— 
Method can internally .Wait() Tasks.
— 
Method cannot internally await Tasks.
— 
Method can return Task<int>.
— 
Method can be awaited on externally.
— 
Can externally catch exceptions thrown by Method when awaited.
•        
async Task<int> Method()
— 
Method can internally contain Tasks.
— 
Method can internally .Wait() Tasks.
— 
Method can internally await Tasks.
— 
Method can return int.
— 
Method can be awaited on externally.
— 
Can externally catch exceptions thrown by Method when awaited.
•        
Task Method()
— 
Method can internally contain Tasks.
— 
Method can internally .Wait() Tasks.
— 
Method cannot internally await Tasks.
— 
Method can return Task.
— 
Method can be awaited on externally.
— 
Can externally catch exceptions thrown by Method when awaited.
•        
async Task Method()
— 
Method can internally contain Tasks.
— 
Method can internally .Wait() Tasks.
— 
Method can internally await Tasks.
— 
Method cannot return anything.
— 
Method can be awaited on externally.
— 
Can externally catch exceptions thrown by Method when awaited.
•        
async void Method()
— 
Method can internally contain Tasks.
— 
Method can internally .Wait() Tasks.
— 
Method can internally await Tasks.
— 
Method cannot return anything.
— 
Method cannot be awaited on externally.
— 
Cannot externally catch exceptions thrown by Method.
— 
Usually used as an async event delegate function definition,
any other situation should use async Task or async Task<T>.
