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.