Sometimes the best way to understand pitfalls in one language is to see how another language prevents them. Tomas Petricek, author of Real-World Functional Programming, discusses six common mistakes found in asynchronous C# code and demonstrates how F# makes them less likely to occur.
While we encourage you read his entire article, titled Async in C# and F#: Asynchronous gotchas in C#, here is a brief synopsis of Tomas covers.
Async does not run asynchronously: Only code that appears after the first await statement is run asynchronously.
Ignoring results: Forgetting to await on the task returned by a function can result in out-of-order execution.
Async void methods: Asynchronous functions that return “async void” instead of “async Task” cannot be awaited, effectively causing the same problem as ignoring results.
Async void lambda functions: This occurs when a function accepts an Action delegate but not a Func<…, Task> delegate. Again, the async function is not going to be awaited on.
Nesting of tasks: In the statement “await Task.Factory.StartNew(async () => { await Task.Delay(1000); });” the first and second await statements are completely unrelated. This means the first await will complete before second await, and the associated 1000 ms delay, are honored.
Not running asynchronously: By using Task.Wait(), the entire call stack can be forced into a synchronous mode.
For those of you unfamiliar with F#, you may be surprised to learn that async workflows in F# are not based on the Task and Task<T> types. Instead, F# uses its own type known as Async<T>.