Version: Unity 6.0 (6000.0)
言語 : 日本語
Awaitable の完了と継続
ジョブシステム

Awaitable のコード例リファレンス

このリファレンスの例は、非同期コードの記述時に経験する一般的なシナリオに対する、Awaitable での解決策を示しています。

非同期テスト

Unity の Test Framework は、Awaitable を有効なテストの戻り値の型として認識しません。しかし、以下の例は、IEnumeratorAwaitable 実装を使用して非同期テストを記述する方法を示しています。

[UnityTest]
public IEnumerator SomeAsyncTest(){
    async Awaitable TestImplementation(){
        // test something with async / await support here
    };
    return TestImplementation();
}

フレームコルーチン

Awaitable クラスのフレーム関連の非同期メソッドを使用して、イテレーターベースのコルーチンの代わりに、非同期の Unity コルーチンを作成できます。

async Awaitable SampleSchedulingJobsForNextFrame()
{
    // Wait until end of frame to avoid competing over resources with other Unity subsystems
    await Awaitable.EndOfFrameAsync(); 
    var jobHandle = ScheduleSomethingWithJobSystem();
    // Let the job execute while the next frame starts
    await Awaitable.NextFrameAsync();
    jobHandle.Complete();
    // Use results of computation
}

JobHandle ScheduleSomethingWithJobSystem()
{
    ...
}

条件付き待機

イテレーターベースのコルーチンでは、デリゲートが true を評価するまで、WaitUntil はコルーチンの実行をサスペンドします。キャンセルトークンを使用して条件が変更されるまでメソッドを待機させることで、Awaitable を返す非同期メソッドと同等の動作を作成できます。

public static async AwaitableUntil(Func<bool> condition, CancellationTokenSource cancellationToken)
{
   while(!condition()){
     cancellationToken.ThrowIfCancellationRequested();
     await Awaitable.NextFrameAsync();
  }
}

その後、以下のようにキャンセルトークンを渡すことができます。

cancellationTokenSource = new CancellationTokenSource();
currentTask = AwaitableUntil(myCondition, cancellationTokenSource.Token);

リソースの非同期ロード

非同期のリソースロード操作を await して、メインスレッドをブロックしないようにすることができます。

public async Awaitable Start()
{
    // Load texture resource asynchronously
    var operation = Resources.LoadAsync("my-texture");
    // Return control to the main thread while the resource loads
    await operation;
    var texture = operation.asset as Texture2D;
}

合成

同じメソッド内で複数の異なる await に互換性のある型を await できます。

public async Awaitable Start()
{
    await CallSomeThirdPartyAPIReturningDotnetTask();
    await Awaitable.NextFrameAsync();
    await SceneManager.LoadSceneAsync("my-scene");
    await SomeUserCodeReturningAwaitable();
    ...
}

.NET タスクでの Awaitable のラッピング

Awaitable の制限のいくつかを回避するために、.NET Task でラップします。これには割り当ての手間がかかりますが、Task API から WhenAllWhenAny などのメソッドにアクセスできます。これを行うには、独自のカスタム AsTask 拡張メソッドを次のように記述します。

// Implement custom AsTask extension methods to wrap Awaitable in Task
public static class AwaitableExtensions
    {
        public static async Task AsTask(this Awaitable a)
        {
            await a;
        }

        public static async Task<T> AsTask<T>(this Awaitable<T> a)
        {
            return await a;
        }
    }

結果を複数回 Await する

AwaitableTask の主な違いは、Awaitable オブジェクトは割り当てを減らすためにプールされることです。Awaitable を返すメソッド (1 つの結果を複数回返すことで完了する) を、安全に await することはできません。いったん返されると、元の Awaitable オブジェクトはプールに返されるためです。

安全ではないバージョン

以下のコードは 安全ではなく、例外とデッドロックの原因になります。

async Awaitable Bar(){
  var taskWithResult = SomeAwaitableReturningFunction();
  var awaitOnce = await taskWithResult;
  // Do something
  // The following will cause errors because at this point taskWithResult has already been pooled back
  var awaitTwice = await taskWithResult;
 }

安全なバージョン

これは、割り当ての手間をかけて、タスクで Awaitable をラップ できるシナリオの 1 つです。こうすると、Task は複数回安全に await できます。

// Implement custom AsTask extension methods to wrap Awaitable in Task
public static class AwaitableExtensions
    {
        public static async Task AsTask(this Awaitable a)
        {
            await a;
        }

        public static async Task<T> AsTask<T>(this Awaitable<T> a)
        {
            return await a;
        }
    }

async Awaitable Bar(){
  var taskWithResult = SomeAwaitableReturningFunction();
  // Wrap the returned Awaitable in a Task
  var taskWithResultAsTask = taskWithResult.AsTask();
  // The task can now be safely awaited multiple times, at the cost of allocating
  var awaitOnce = await taskWithResultAsTask;
  // Do something
  var awaitTwice = await taskWithResultAsTask;
 }

追加リソース

Awaitable の完了と継続
ジョブシステム