async/await 非同期メソッドの待ちかた調査

# async/await 非同期メソッドの待ちかた

async/awaitを使うと数珠繋ぎでasync/awaitが連鎖しがち。
そんな時に非同期処理を同期として呼び出したい場合に以下の2パターンが多い様子。。

* task.GetAwaiter().GetResult()
* task.Wait()

例外で差分があるようなので比較した。

## Wait()

### ソース

~~~ C#
namespace Sandbox
{
    using System;
    using System.Threading.Tasks;

    class Program
    {
        static async Task Main(string[] args)
        {
            try
            {
                Console.WriteLine($"{DateTime.Now} start");
                Task.Run(HeavyFunctionAsync).Wait();
                Console.WriteLine($"{DateTime.Now} end");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        public static async Task HeavyFunctionAsync()
        {
            for (var i = 0; i < 5; i++)
            {
                await Task.Delay(1000);
                Console.WriteLine($"{DateTime.Now} N={i}");
            }

            throw new InvalidOperationException("exception");

            return;
        }
    }
}
~~~

### 結果

AggregrateExceptinoとして捕捉。InnerException見る必要があるのがメンドクサイ。

~~~
2021/06/05 21:30:30 start
2021/06/05 21:30:32 N=0
2021/06/05 21:30:33 N=1
2021/06/05 21:30:34 N=2
2021/06/05 21:30:35 N=3
2021/06/05 21:30:36 N=4
System.AggregateException: One or more errors occurred. (exception)
 ---> System.InvalidOperationException: exception
   at Sandbox.Program.HeavyFunctionAsync() in D:\work\Repos\Sandbox\Sandbox\Program.cs:line 30
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()
   at Sandbox.Program.Main(String[] args) in D:\work\Repos\Sandbox\Sandbox\Program.cs:line 13
~~~


## GetAwaiter().GetResult()

### ソース。

~~~ c#
namespace Sandbox
{
    using System;
    using System.Threading.Tasks;

    class Program
    {
        static async Task Main(string[] args)
        {
            try
            {
                Console.WriteLine($"{DateTime.Now} start");
                Task.Run(HeavyFunctionAsync).GetAwaiter().GetResult();
                Console.WriteLine($"{DateTime.Now} end");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        public static async Task HeavyFunctionAsync()
        {
            for (var i = 0; i < 5; i++)
            {
                await Task.Delay(1000);
                Console.WriteLine($"{DateTime.Now} N={i}");
            }

            throw new InvalidOperationException("exception");

            return;
        }
    }
}
~~~

### 結果

InvalidOperationExceptionとして捕捉。

~~~
2021/06/05 21:27:48 start
2021/06/05 21:27:49 N=0
2021/06/05 21:27:50 N=1
2021/06/05 21:27:51 N=2
2021/06/05 21:27:52 N=3
2021/06/05 21:27:53 N=4
System.InvalidOperationException: exception
   at Sandbox.Program.HeavyFunctionAsync() in D:\work\Repos\Sandbox\Sandbox\Program.cs:line 30
   at Sandbox.Program.Main(String[] args) in D:\work\Repos\Sandbox\Sandbox\Program.cs:line 13
~~~

# 結論

Wait()は良くある別タスク内で発生した例外同様AggreateExceptinoでまとめてChatchされる。
一方でGetAwaiter().GetResult()は同期メソッドで例外が発生したかの如く、発生時の状態で補足される。

個人的にはGetAwaiter().GetResult()の方が好きだが別意見の人もいそう