時々 1 つの言語の落とし穴を理解する最良の方法は、別の言語がそれらをどのように防いでいるかを参考にすることである。Real-World Functional Programmingの著者であるTomas Petricek氏は、非同期なC# コードに見られる6つの一般的な誤りについて説明し、どのように F# がそれらの発生する可能性を少なくしているかを示している。
彼のAsync in C# and F#: Asynchronous gotchas in C#という題名の記事を全て読むことを薦めるが、以下がその概要である。
Asyncは、非同期に動かない: 最初のawait ステートメントの後に出てきたコードだけが非同期に動く。
結果を無視する: 関数によって返されるタスク上でawaitすることを忘れると、順番のでたらめな結果になる。
Async void メソッド: “async Task”でなく“async void”を返す関数は、awaitできないので、結果を無視した場合と同じ問題を起こすことになる。
Async void ラムダ関数: これは、関数が Action delegateを受け付けて、 Func<…, Task> delegateを受け付けない時に起こる。またしても、非同期関数はawaitされない。
入れ子のタスク: “await Task.Factory.StartNew(async () => { await Task.Delay(1000); });” というステートメント中の最初と2番目の await ステートメントは完全に無関係である。すなわち最初のawaitが2番目のawaitの前に終了する。関連付けられた1000 msの遅延は、有効である。
非同期に走らない: Task.Wait()を使うと、全コールスタックは、強制的に同期モードになる。
F#に不慣れな人は、F#の非同期ワークフローがTask と Task<T> 型をベースにしていないのを知って驚くだろう。代わりに F# Async<T>として知られている独自の型を使っている。