Cysharp / UniTask

Provides an efficient allocation free async/await integration for Unity.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

R3 Observable as UniTask never completes

Antoshidza opened this issue · comments

UnityEngine.Debug.Log($"start to await num selection");
await Observable.EveryUpdate() // never completes
    .Select(_ => GetNumberKeyPressed())
    .Where(numberKey => numberKey != -1)
    .AsSystemObservable()
    .ToUniTask();
// await UniTask.Delay(1000); // works
UnityEngine.Debug.Log($"await completed"); // never happens

In R3 there is only Observable class so I can't use .ToUniTask() directly, instead I try to use .AsSystemObservable before but such code never completed. Observable query itself also works when subscribed with classic R3 .Subscribe method. At the same time simple Delay works perfectly. Am I doing something wrong or there is no way to treat R3 observable as UniTask?

Unity 2023.2.15
R3 1.1.11
UniTask 2.5.4

if .ToUniTask(true) called with true passed then all works well. If look inside this extension method it reveals that if firstValue parameter is true then FirstValueToUniTaskObserver is created instead of ToUniTaskObserver. The difference as I can tell is when this two IObservable calls TrySetResult on theirs UniTaskCompletionSource<T> promise. FirstValueToUniTaskObserver do it straightaway in OnNext and ToUniTaskObserver do it inside OnComplete. So it seems that OnComplete doesn't get called.

I see. I'd like to think about that conversion path, but semantically,
how about converting R3 to Task and then having it converted to UniTask?

I see. I'd like to think about that conversion path, but semantically, how about converting R3 to Task and then having it converted to UniTask?

Does this Observable<T> -> Task<T> -> UniTask<T> available today? I see R3 has extensions like FirstAsync which I believe allow to await first callback.

If yes then it is already good enough to bridge R3 and UniTask. But as I understand UniTask stands for optimization and integration into unity's player loop. When we first create Task<T> from R3 we ruin optimization part I believe. Am I wrong?

Obviously R3 nor UniTask shouldn't know about each other, which is why we need some bridge. Guess separate package would be overkill for such little extension. Maybe some code gist would be enough for folks like me who want to use those packages in a tight couple.

In R3, FirstAsync or LastAsync allows you to decide which way to wait.
Usually in conver to Task, the code expects Last, but First is often more useful.

It is natural to want to convert R3 to an awaitable type (that is why FirstAsync and LastAsync exist).
It is true that there is an overhead due to not being able to convert directly, but we do not think it is large enough to be a problem in situations where this is required.