【Unity】【R3】IAsyncEnumerableからストリームを作成する

R3でIAsyncEnumerableからストリームを作成する方法です。

Unity 2022.3.28f1
R3 1.2.9

はじめに

R3では非同期メソッド版のObservable.Createを使ってストリームを作成できるようになりました。

light11.hatenadiary.com

これとは別に、IAsyncEnumerableからストリームを作成する方法もあります。

本記事ではこの方法についてまとめます。

使い方

早速使い方です。

using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using Cysharp.Threading.Tasks;
using R3;
using UnityEngine;

public sealed class Example : MonoBehaviour
{
    private void Start()
    {
        // Observable.CreateFromにIAsyncEnumerableを渡す
        Observable.CreateFrom(ExampleAsync)
            .Subscribe(x => { Debug.Log($"OnNext: {x}"); },
                       result => { Debug.Log($"OnCompleted: {result.IsSuccess}"); })
            .AddTo(this);
    }

    private static async IAsyncEnumerable<Unit> ExampleAsync(
        [EnumeratorCancellation] CancellationToken cancellationToken
    )
    {
        // 1秒ごとに現在時刻を通知、10回で終わり
        for (var i = 0; i < 10; i++)
        {
            await UniTask.Delay(1000, cancellationToken: cancellationToken);
            yield return Unit.Default;
        }
    }
}

このケースでは上に記事をリンクしたObservable.Createを使ったものよりもシンプルになっています。

エラーの扱い

IAsyncEnumerableを返すメソッド内でスローされた例外は、OnCompletedに通知されます。
以下のようにコードを変更して再生すると3回通知があった後にIsSuccessFalseになったOnCompleteが通知されることを確認できます。

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using Cysharp.Threading.Tasks;
using R3;
using UnityEngine;

public sealed class Example : MonoBehaviour
{
    private void Start()
    {
        Observable.CreateFrom(ExampleAsync)
            .Subscribe(x => { Debug.Log($"OnNext: {x}"); },
                       result => { Debug.Log($"OnCompleted: {result.IsSuccess}"); })
            .AddTo(this);
    }

    private static async IAsyncEnumerable<Unit> ExampleAsync(
        [EnumeratorCancellation] CancellationToken cancellationToken
    )
    {
        for (var i = 0; i < 10; i++)
        {
            if (i == 3)
            {
                throw new Exception("Error!");
            }

            await UniTask.Delay(1000, cancellationToken: cancellationToken);
            yield return Unit.Default;
        }
    }
}

関連

light11.hatenadiary.com