Excel-DNAで非同期処理の関数をつくる

今回はExcel-DNAで非同期処理をつくってみます。
Excel-DNAの使い方は以下。

https://qiita.com/msms/items/7de38ab10799af195c62


# 非同期処理を追加する
- [このページを参考に](https://excel-dna.net/docs/guides-basic/asynchronous-functions)追加していきます
- `<PackageReference Include="ExcelDna.Addin" Version="1.8.0" />`

## 必要ファイルの追加
[このファイル](https://github.com/Excel-DNA/Registration/blob/master/Source/ExcelDna.Registration/Utils/AsyncTaskUtil.cs)をそのまま使います。

```AsyncTaskUtil.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using ExcelDna.Integration;

namespace ExcelDna.Registration.Utils
{
    // Helpers for creating and using Task-based functions with Excel-DNA's RTD-based IObservable support
    public static class AsyncTaskUtil
    {
        public static object RunTask<TResult>(string callerFunctionName, object callerParameters, Func<Task<TResult>> taskSource)
        {
            return ExcelAsyncUtil.RunTask(callerFunctionName, callerParameters, taskSource);
        }

        // Careful - this might only work as long as the task is not shared between calls, since cancellation cancels that task
        public static object RunTaskWithCancellation<TResult>(string callerFunctionName, object callerParameters, Func<CancellationToken, Task<TResult>> taskSource)
        {
            return ExcelAsyncUtil.RunTaskWithCancellation(callerFunctionName, callerParameters, taskSource);
        }

        public static object RunAsTask<TResult>(string callerFunctionName, object callerParameters, Func<TResult> function)
        {
            return ExcelAsyncUtil.RunAsTask(callerFunctionName, callerParameters, function);
        }

        public static object RunAsTaskWithCancellation<TResult>(string callerFunctionName, object callerParameters, Func<CancellationToken, TResult> function)
        {
            return ExcelAsyncUtil.RunAsTaskWithCancellation(callerFunctionName, callerParameters, function);
        }
    }
}
```

## 非同期処理の関数
指定ミリ秒待機して、指定文字列を返します。

```ExcelDnaAddin.cs
using ExcelDna.Integration;
using ExcelDna.Registration.Utils;

namespace TestExcelDna;

public static class MyFunctions
{
    [ExcelFunction(Description = "非同期関数です。ミリ秒待機して文字列を返します。")]
    public static object SayDelay(string targetValue, int msTime)
    {
        var functionName = nameof(SayDelayBefore);
        var parameters = new object[] { targetValue, msTime };
        return AsyncTaskUtil.RunTask(functionName, parameters, async () =>
        {
            return await DnaDelayedTask(targetValue, msTime);
        });
    }
    
    public static async Task<string> DnaDelayedTask(string targetValue, int msDelay)
    {
        await Task.Delay(msDelay);
        return targetValue + msDelay;
    }
}
```

## 非同期処理中のセル表示
上記の方法だと処理中のセルに`#N/A`と表示されて、処理後に結果が表示される。
そのため、処理中なのかエラーなのかが分かりにくくなります。
以下へ変更する事で、処理中だと分かるようにセルへ表示するようにします。

以下を参考にしました。
https://groups.google.com/g/exceldna/c/3t8Q616k8QA

- `private static readonly object ReturnNA = new();`を追加
- ` var result = AsyncTaskUtil.RunTask(`で結果を受け取るようにしておき、`if (result.Equals(ExcelError.ExcelErrorNA))`とする
- これにより処理中は、`Busy...`と表示されて、処理完了すればその結果が表示されるようになります

```c#
private static readonly object ReturnNA = new();
[ExcelFunction(Description = "非同期関数です。ミリ秒待機して文字列を返します。")]
public static string SayDelay(string targetValue, int msTime)
{
    var functionName = nameof(SayDelay);
    var parameters = new object[] { targetValue, msTime };
    var result = AsyncTaskUtil.RunTask(functionName, parameters, async () =>
      {
          return await DnaDelayedTask(targetValue, msTime);
      });

    if (result.Equals(ReturnNA))
    {
        return ExcelError.ExcelErrorNA.ToString();
    }
    if (result.Equals(ExcelError.ExcelErrorNA))
        return "Busy...";
    else
        return (string)result;
}
```

## 呼び出し結果
- セル表示の変更前
![testDelayBefore.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2383487/34e511e9-5ec8-98e6-3749-a18ca82537c1.gif)

- セル表示の変更後
![testDelay.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2383487/940bbe7c-67c3-75a3-df09-fd0219d08ce6.gif)


# 参考
- https://qiita.com/hiro_t/items/f37256a59946c6c0faa7
- https://github.com/sponsors/Excel-DNA