From ee58aab0a9f4878d5c96faf9c04647110881e058 Mon Sep 17 00:00:00 2001 From: neuecc Date: Sun, 17 May 2020 01:29:45 +0900 Subject: [PATCH] Add AsyncReactiveProperty --- src/UniTask.NetCore/UniTask.NetCore.csproj | 1 + .../UniTask/Runtime/AsyncReactiveProperty.cs | 120 ++++++++ .../Runtime/AsyncReactiveProperty.cs.meta | 11 + .../Plugins/UniTask/Runtime/TriggerEvent.cs | 229 +++++++++++++++ .../UniTask/Runtime/TriggerEvent.cs.meta | 11 + .../Runtime/Triggers/AsyncTriggerBase.cs | 208 +------------- .../Plugins/UniTask/Runtime/UniTask.asmdef | 8 +- .../UniTask/Runtime/UnityBindingExtensions.cs | 263 ++++++++++++++++++ .../Runtime/UnityBindingExtensions.cs.meta | 11 + src/UniTask/Assets/Scenes/SandboxMain.cs | 210 ++------------ 10 files changed, 672 insertions(+), 400 deletions(-) create mode 100644 src/UniTask/Assets/Plugins/UniTask/Runtime/AsyncReactiveProperty.cs create mode 100644 src/UniTask/Assets/Plugins/UniTask/Runtime/AsyncReactiveProperty.cs.meta create mode 100644 src/UniTask/Assets/Plugins/UniTask/Runtime/TriggerEvent.cs create mode 100644 src/UniTask/Assets/Plugins/UniTask/Runtime/TriggerEvent.cs.meta create mode 100644 src/UniTask/Assets/Plugins/UniTask/Runtime/UnityBindingExtensions.cs create mode 100644 src/UniTask/Assets/Plugins/UniTask/Runtime/UnityBindingExtensions.cs.meta diff --git a/src/UniTask.NetCore/UniTask.NetCore.csproj b/src/UniTask.NetCore/UniTask.NetCore.csproj index 67ca673..23b0cda 100644 --- a/src/UniTask.NetCore/UniTask.NetCore.csproj +++ b/src/UniTask.NetCore/UniTask.NetCore.csproj @@ -28,6 +28,7 @@ ..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.cs; ..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.uGUI.cs; ..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.MonoBehaviour.cs; +..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs; " /> diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/AsyncReactiveProperty.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/AsyncReactiveProperty.cs new file mode 100644 index 0000000..b6bb90e --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/AsyncReactiveProperty.cs @@ -0,0 +1,120 @@ +using Cysharp.Threading.Tasks.Linq; +using System; +using System.Threading; + +namespace Cysharp.Threading.Tasks +{ + public interface IAsyncReadOnlyReactiveProperty : IUniTaskAsyncEnumerable + { + T Value { get; } + } + + public interface IAsyncReactiveProperty : IAsyncReadOnlyReactiveProperty + { + new T Value { get; set; } + } + + [Serializable] + public class AsyncReactiveProperty : IUniTaskAsyncEnumerable, IDisposable + { + TriggerEvent triggerEvent; + +#if UNITY_2018_3_OR_NEWER + [UnityEngine.SerializeField] +#endif + T latestValue; + + public T Value + { + get + { + return latestValue; + } + set + { + this.latestValue = value; + triggerEvent.SetResult(value); + } + } + + public AsyncReactiveProperty(T value) + { + this.latestValue = value; + this.triggerEvent = default; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) + { + return new Enumerator(this, cancellationToken); + } + + public void Dispose() + { + triggerEvent.SetCanceled(CancellationToken.None); + } + + sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator, IResolveCancelPromise + { + static Action cancellationCallback = CancellationCallback; + + readonly AsyncReactiveProperty parent; + readonly CancellationToken cancellationToken; + readonly CancellationTokenRegistration cancellationTokenRegistration; + T value; + bool isDisposed; + + public Enumerator(AsyncReactiveProperty parent, CancellationToken cancellationToken) + { + this.parent = parent; + this.cancellationToken = cancellationToken; + + parent.triggerEvent.Add(this); + TaskTracker.TrackActiveTask(this, 3); + + if (cancellationToken.CanBeCanceled) + { + cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this); + } + } + + public T Current => value; + + public UniTask MoveNextAsync() + { + completionSource.Reset(); + return new UniTask(this, completionSource.Version); + } + + public UniTask DisposeAsync() + { + if (!isDisposed) + { + isDisposed = true; + TaskTracker.RemoveTracking(this); + completionSource.TrySetCanceled(cancellationToken); + parent.triggerEvent.Remove(this); + } + return default; + } + + public bool TrySetResult(T value) + { + this.value = value; + completionSource.TrySetResult(true); + return true; + } + + public bool TrySetCanceled(CancellationToken cancellationToken = default) + { + DisposeAsync().Forget(); + return true; + } + + static void CancellationCallback(object state) + { + var self = (Enumerator)state; + self.DisposeAsync().Forget(); + } + } + } +} diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/AsyncReactiveProperty.cs.meta b/src/UniTask/Assets/Plugins/UniTask/Runtime/AsyncReactiveProperty.cs.meta new file mode 100644 index 0000000..d64e3cf --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/AsyncReactiveProperty.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8ef320b87f537ee4fb2282e765dc6166 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/TriggerEvent.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/TriggerEvent.cs new file mode 100644 index 0000000..65ec084 --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/TriggerEvent.cs @@ -0,0 +1,229 @@ +using Cysharp.Threading.Tasks.Internal; +using System; +using System.Threading; + +namespace Cysharp.Threading.Tasks +{ + public interface ITriggerEvent + { + void SetResult(T value); + void SetCanceled(CancellationToken cancellationToken); + void Add(IResolveCancelPromise handler); + void Remove(IResolveCancelPromise handler); + } + + // be careful to use, itself is struct. + public struct TriggerEvent : ITriggerEvent + { + // optimize: many cases, handler is single. + IResolveCancelPromise singleHandler; + + IResolveCancelPromise[] handlers; + + // when running(in TrySetResult), does not add immediately. + bool isRunning; + IResolveCancelPromise waitHandler; + MinimumQueue> waitQueue; + + public void SetResult(T value) + { + isRunning = true; + + if (singleHandler != null) + { + try + { + singleHandler.TrySetResult(value); + } + catch (Exception ex) + { +#if UNITY_2018_3_OR_NEWER + UnityEngine.Debug.LogException(ex); +#else + Console.WriteLine(ex); +#endif + } + } + + if (handlers != null) + { + for (int i = 0; i < handlers.Length; i++) + { + if (handlers[i] != null) + { + try + { + handlers[i].TrySetResult(value); + } + catch (Exception ex) + { + handlers[i] = null; +#if UNITY_2018_3_OR_NEWER + UnityEngine.Debug.LogException(ex); +#else + Console.WriteLine(ex); +#endif + } + } + } + } + + isRunning = false; + + if (waitHandler != null) + { + var h = waitHandler; + waitHandler = null; + Add(h); + } + + if (waitQueue != null) + { + while (waitQueue.Count != 0) + { + Add(waitQueue.Dequeue()); + } + } + } + + public void SetCanceled(CancellationToken cancellationToken) + { + isRunning = true; + + if (singleHandler != null) + { + try + { + ((ICancelPromise)singleHandler).TrySetCanceled(cancellationToken); + } + catch (Exception ex) + { +#if UNITY_2018_3_OR_NEWER + UnityEngine.Debug.LogException(ex); +#else + Console.WriteLine(ex); +#endif + } + } + + if (handlers != null) + { + for (int i = 0; i < handlers.Length; i++) + { + if (handlers[i] != null) + { + try + { + ((ICancelPromise)handlers[i]).TrySetCanceled(cancellationToken); + } + catch (Exception ex) + { +#if UNITY_2018_3_OR_NEWER + UnityEngine.Debug.LogException(ex); +#else + Console.WriteLine(ex); +#endif + handlers[i] = null; + } + } + } + } + + isRunning = false; + + if (waitHandler != null) + { + var h = waitHandler; + waitHandler = null; + Add(h); + } + + if (waitQueue != null) + { + while (waitQueue.Count != 0) + { + Add(waitQueue.Dequeue()); + } + } + } + + public void Add(IResolveCancelPromise handler) + { + if (isRunning) + { + if (waitHandler == null) + { + waitHandler = handler; + return; + } + + if (waitQueue == null) + { + waitQueue = new MinimumQueue>(4); + } + waitQueue.Enqueue(handler); + return; + } + + if (singleHandler == null) + { + singleHandler = handler; + } + else + { + if (handlers == null) + { + handlers = new IResolveCancelPromise[4]; + } + + // check empty + for (int i = 0; i < handlers.Length; i++) + { + if (handlers[i] == null) + { + handlers[i] = handler; + return; + } + } + + // full, ensure capacity + var last = handlers.Length; + { + EnsureCapacity(ref handlers); + handlers[last] = handler; + } + } + } + + static void EnsureCapacity(ref IResolveCancelPromise[] array) + { + var newSize = array.Length * 2; + var newArray = new IResolveCancelPromise[newSize]; + Array.Copy(array, 0, newArray, 0, array.Length); + array = newArray; + } + + public void Remove(IResolveCancelPromise handler) + { + if (singleHandler == handler) + { + singleHandler = null; + } + else + { + if (handlers != null) + { + for (int i = 0; i < handlers.Length; i++) + { + if (handlers[i] == handler) + { + // fill null. + handlers[i] = null; + return; + } + } + } + } + } + } +} diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/TriggerEvent.cs.meta b/src/UniTask/Assets/Plugins/UniTask/Runtime/TriggerEvent.cs.meta new file mode 100644 index 0000000..bbd47af --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/TriggerEvent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f68b22bb8f66f5c4885f9bd3c4fc43ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/Triggers/AsyncTriggerBase.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/Triggers/AsyncTriggerBase.cs index a8a34b3..1fa1a42 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/Triggers/AsyncTriggerBase.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/Triggers/AsyncTriggerBase.cs @@ -1,6 +1,5 @@ #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member -using Cysharp.Threading.Tasks.Internal; using Cysharp.Threading.Tasks.Linq; using System; using System.Threading; @@ -25,7 +24,7 @@ namespace Cysharp.Threading.Tasks.Triggers if (calledDestroy) return; calledDestroy = true; - triggerEvent.TrySetCanceled(CancellationToken.None); + triggerEvent.SetCanceled(CancellationToken.None); } internal void AddHandler(IResolveCancelPromise handler) @@ -50,7 +49,7 @@ namespace Cysharp.Threading.Tasks.Triggers protected void RaiseEvent(T value) { - triggerEvent.TrySetResult(value); + triggerEvent.SetResult(value); } public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) @@ -284,207 +283,4 @@ namespace Cysharp.Threading.Tasks.Triggers core.OnCompleted(continuation, state, token); } } - - // be careful to use, itself is struct. - public struct TriggerEvent - { - // optimize: many cases, handler is single. - IResolveCancelPromise singleHandler; - - IResolveCancelPromise[] handlers; - - // when running(in TrySetResult), does not add immediately. - bool isRunning; - IResolveCancelPromise waitHandler; - MinimumQueue> waitQueue; - - public bool TrySetResult(T value) - { - isRunning = true; - - if (singleHandler != null) - { - try - { - singleHandler.TrySetResult(value); - } - catch (Exception ex) - { - Debug.LogException(ex); - } - } - - if (handlers != null) - { - for (int i = 0; i < handlers.Length; i++) - { - if (handlers[i] != null) - { - try - { - handlers[i].TrySetResult(value); - } - catch (Exception ex) - { - handlers[i] = null; - Debug.LogException(ex); - } - } - } - } - - isRunning = false; - - if (waitHandler != null) - { - var h = waitHandler; - waitHandler = null; - Add(h); - } - - if (waitQueue != null) - { - while (waitQueue.Count != 0) - { - Add(waitQueue.Dequeue()); - } - } - - return true; - } - - public bool TrySetCanceled(CancellationToken cancellationToken) - { - isRunning = true; - - if (singleHandler != null) - { - try - { - ((ICancelPromise)singleHandler).TrySetCanceled(cancellationToken); - } - catch (Exception ex) - { - Debug.LogException(ex); - } - } - - if (handlers != null) - { - for (int i = 0; i < handlers.Length; i++) - { - if (handlers[i] != null) - { - try - { - ((ICancelPromise)handlers[i]).TrySetCanceled(cancellationToken); - } - catch (Exception ex) - { - Debug.LogException(ex); - handlers[i] = null; - } - } - } - } - - isRunning = false; - - if (waitHandler != null) - { - var h = waitHandler; - waitHandler = null; - Add(h); - } - - if (waitQueue != null) - { - while (waitQueue.Count != 0) - { - Add(waitQueue.Dequeue()); - } - } - - return true; - } - - public void Add(IResolveCancelPromise handler) - { - if (isRunning) - { - if (waitHandler == null) - { - waitHandler = handler; - return; - } - - if (waitQueue == null) - { - waitQueue = new MinimumQueue>(4); - } - waitQueue.Enqueue(handler); - return; - } - - if (singleHandler == null) - { - singleHandler = handler; - } - else - { - if (handlers == null) - { - handlers = new IResolveCancelPromise[4]; - } - - // check empty - for (int i = 0; i < handlers.Length; i++) - { - if (handlers[i] == null) - { - handlers[i] = handler; - return; - } - } - - // full, ensure capacity - var last = handlers.Length; - { - EnsureCapacity(ref handlers); - handlers[last] = handler; - } - } - } - - static void EnsureCapacity(ref IResolveCancelPromise[] array) - { - var newSize = array.Length * 2; - var newArray = new IResolveCancelPromise[newSize]; - Array.Copy(array, 0, newArray, 0, array.Length); - array = newArray; - } - - public void Remove(IResolveCancelPromise handler) - { - if (singleHandler == handler) - { - singleHandler = null; - } - else - { - if (handlers != null) - { - for (int i = 0; i < handlers.Length; i++) - { - if (handlers[i] == handler) - { - // fill null. - handlers[i] = null; - return; - } - } - } - } - } - } } \ No newline at end of file diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.asmdef b/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.asmdef index 75ee3ed..12c99ed 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.asmdef +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.asmdef @@ -1,7 +1,8 @@ { "name": "UniTask", "references": [ - "Unity.ResourceManager" + "Unity.ResourceManager", + "Unity.TextMeshPro" ], "includePlatforms": [], "excludePlatforms": [], @@ -15,6 +16,11 @@ "name": "com.unity.addressables", "expression": "", "define": "UNITASK_ADDRESSABLE_SUPPORT" + }, + { + "name": "com.unity.textmeshpro", + "expression": "", + "define": "UNITASK_TEXTMESHPRO_SUPPORT" } ], "noEngineReferences": false diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityBindingExtensions.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityBindingExtensions.cs new file mode 100644 index 0000000..510763d --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityBindingExtensions.cs @@ -0,0 +1,263 @@ +using System; +using System.Threading; +using UnityEngine.UI; + +namespace Cysharp.Threading.Tasks +{ + public static class UnityBindingExtensions + { + // -> Text + + public static void BindTo(this IUniTaskAsyncEnumerable source, Text text, bool rebindOnError = true) + { + BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); + } + + public static void BindTo(this IUniTaskAsyncEnumerable source, Text text, CancellationToken cancellationToken, bool rebindOnError = true) + { + BindToCore(source, text, cancellationToken, rebindOnError).Forget(); + } + + static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable source, Text text, CancellationToken cancellationToken, bool rebindOnError) + { + var repeat = false; + BIND_AGAIN: + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + while (true) + { + bool moveNext; + try + { + moveNext = await e.MoveNextAsync(); + repeat = false; + } + catch (Exception ex) + { + if (ex is OperationCanceledException) return; + + if (rebindOnError && !repeat) + { + repeat = true; + if (e != null) + { + await e.DisposeAsync(); + } + + goto BIND_AGAIN; + } + else + { + throw; + } + } + + if (!moveNext) return; + + text.text = e.Current; + } + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + } + + // -> Text + + public static void BindTo(this IUniTaskAsyncEnumerable source, Text text, bool rebindOnError = true) + { + BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); + } + + public static void BindTo(this IUniTaskAsyncEnumerable source, Text text, CancellationToken cancellationToken, bool rebindOnError = true) + { + BindToCore(source, text, cancellationToken, rebindOnError).Forget(); + } + + public static void BindTo(this AsyncReactiveProperty source, Text text, bool rebindOnError = true) + { + BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); + } + + static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable source, Text text, CancellationToken cancellationToken, bool rebindOnError) + { + var repeat = false; + BIND_AGAIN: + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + while (true) + { + bool moveNext; + try + { + moveNext = await e.MoveNextAsync(); + repeat = false; + } + catch (Exception ex) + { + if (ex is OperationCanceledException) return; + + if (rebindOnError && !repeat) + { + repeat = true; + if (e != null) + { + await e.DisposeAsync(); + } + + goto BIND_AGAIN; + } + else + { + throw; + } + } + + if (!moveNext) return; + + text.text = e.Current.ToString(); + } + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + } + + // -> Selectable + + public static void BindTo(this IUniTaskAsyncEnumerable source, Selectable selectable, bool rebindOnError = true) + { + BindToCore(source, selectable, selectable.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); + } + + public static void BindTo(this IUniTaskAsyncEnumerable source, Selectable selectable, CancellationToken cancellationToken, bool rebindOnError = true) + { + BindToCore(source, selectable, cancellationToken, rebindOnError).Forget(); + } + + static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable source, Selectable selectable, CancellationToken cancellationToken, bool rebindOnError) + { + var repeat = false; + BIND_AGAIN: + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + while (true) + { + bool moveNext; + try + { + moveNext = await e.MoveNextAsync(); + repeat = false; + } + catch (Exception ex) + { + if (ex is OperationCanceledException) return; + + if (rebindOnError && !repeat) + { + repeat = true; + if (e != null) + { + await e.DisposeAsync(); + } + + goto BIND_AGAIN; + } + else + { + throw; + } + } + + if (!moveNext) return; + + + selectable.interactable = e.Current; + } + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + } + + // TMP + +#if UNITASK_TEXTMESHPRO_SUPPORT + + public static void BindTo(this IUniTaskAsyncEnumerable source, TMPro.TMP_Text text, bool rebindOnError = true) + { + BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); + } + + public static void BindTo(this IUniTaskAsyncEnumerable source, TMPro.TMP_Text text, CancellationToken cancellationToken, bool rebindOnError = true) + { + BindToCore(source, text, cancellationToken, rebindOnError).Forget(); + } + + static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable source, TMPro.TMP_Text text, CancellationToken cancellationToken, bool rebindOnError) + { + var repeat = false; + BIND_AGAIN: + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + while (true) + { + bool moveNext; + try + { + moveNext = await e.MoveNextAsync(); + repeat = false; + } + catch (Exception ex) + { + if (ex is OperationCanceledException) return; + + if (rebindOnError && !repeat) + { + repeat = true; + if (e != null) + { + await e.DisposeAsync(); + } + + goto BIND_AGAIN; + } + else + { + throw; + } + } + + if (!moveNext) return; + + text.text = e.Current; + } + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + } + +#endif + } +} diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityBindingExtensions.cs.meta b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityBindingExtensions.cs.meta new file mode 100644 index 0000000..3fae798 --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityBindingExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 090b20e3528552b4a8d751f7df525c2b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UniTask/Assets/Scenes/SandboxMain.cs b/src/UniTask/Assets/Scenes/SandboxMain.cs index 31d7740..f9e208a 100644 --- a/src/UniTask/Assets/Scenes/SandboxMain.cs +++ b/src/UniTask/Assets/Scenes/SandboxMain.cs @@ -14,6 +14,7 @@ using Unity.Jobs; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; +using TMPro; public struct MyJob : IJob { @@ -39,187 +40,9 @@ public enum MyEnum -public interface IAsyncReadOnlyReactiveProperty : IUniTaskAsyncEnumerable -{ - T Value { get; } -} - - -public interface IAsyncReactiveProperty : IAsyncReadOnlyReactiveProperty -{ - new T Value { get; set; } -} - - -[Serializable] -public struct AsyncValueReactiveProperty : IUniTaskAsyncEnumerable -{ - TriggerEvent triggerEvent; - - [SerializeField] - T latestValue; - - public T Value - { - get - { - return latestValue; - } - set - { - this.latestValue = value; - triggerEvent.TrySetResult(value); - } - } - - public AsyncValueReactiveProperty(T value) - { - this.latestValue = value; - this.triggerEvent = default; - } - - public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) - { - return new Enumerator(triggerEvent, cancellationToken); - } - - public class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator, IResolveCancelPromise - { - static Action cancellationCallback = CancellationCallback; - - readonly TriggerEvent triggerEvent; - readonly CancellationToken cancellationToken; - readonly CancellationTokenRegistration cancellationTokenRegistration; - T value; - - public Enumerator(TriggerEvent triggerEvent, CancellationToken cancellationToken) - { - this.triggerEvent = triggerEvent; - this.cancellationToken = cancellationToken; - - triggerEvent.Add(this); - - if (cancellationToken.CanBeCanceled) - { - cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this); - } - } - - public T Current => value; - - public UniTask MoveNextAsync() - { - completionSource.Reset(); - return new UniTask(this, completionSource.Version); - } - - public UniTask DisposeAsync() - { - triggerEvent.TrySetCanceled(cancellationToken); - triggerEvent.Remove(this); - return default; - } - - public bool TrySetResult(T value) - { - this.value = value; - return triggerEvent.TrySetResult(value); - } - - public bool TrySetCanceled(CancellationToken cancellationToken = default) - { - DisposeAsync().Forget(); - return true; - } - - static void CancellationCallback(object state) - { - var self = (Enumerator)state; - self.DisposeAsync().Forget(); - } - } -} public static partial class UnityUIComponentExtensions -{ - public static void BindTo(this IUniTaskAsyncEnumerable source, Text text, bool rebindOnError = true) - { - BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); - } - - public static void BindTo(this IUniTaskAsyncEnumerable source, Text text, CancellationToken cancellationToken, bool rebindOnError = true) - { - BindToCore(source, text, cancellationToken, rebindOnError).Forget(); - } - - static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable source, Text text, CancellationToken cancellationToken, bool rebindOnError) - { - var repeat = false; - BIND_AGAIN: - var e = source.GetAsyncEnumerator(cancellationToken); - try - { - while (true) - { - bool moveNext; - try - { - moveNext = await e.MoveNextAsync(); - repeat = false; - } - catch (Exception ex) - { - if (ex is OperationCanceledException) return; - - if (rebindOnError && !repeat) - { - repeat = true; - if (e != null) - { - await e.DisposeAsync(); - } - - goto BIND_AGAIN; - } - else - { - throw; - } - } - - if (!moveNext) return; - - text.text = e.Current; - } - } - finally - { - if (e != null) - { - await e.DisposeAsync(); - } - } - } - - //public static IDisposable SubscribeToText(this IObservable source, Text text) - //{ - // return source.SubscribeWithState(text, (x, t) => t.text = x.ToString()); - //} - - //public static IDisposable SubscribeToText(this IObservable source, Text text, Func selector) - //{ - // return source.SubscribeWithState2(text, selector, (x, t, s) => t.text = s(x)); - //} - - //public static IDisposable SubscribeToInteractable(this IObservable source, Selectable selectable) - //{ - // return source.SubscribeWithState(selectable, (x, s) => s.interactable = x); - //} -} - - -public static class MyClass { } @@ -230,7 +53,6 @@ public static class MyClass - public class SandboxMain : MonoBehaviour { public Button okButton; @@ -239,6 +61,9 @@ public class SandboxMain : MonoBehaviour CancellationTokenSource cts; + public AsyncReactiveProperty RP1; + + UniTaskCompletionSource ucs; async UniTask FooAsync() { @@ -300,7 +125,7 @@ public class SandboxMain : MonoBehaviour } - async void Start() + void Start() { Application.SetStackTraceLogType(LogType.Error, StackTraceLogType.Full); Application.SetStackTraceLogType(LogType.Exception, StackTraceLogType.Full); @@ -309,6 +134,13 @@ public class SandboxMain : MonoBehaviour //ShowPlayerLoop.DumpPlayerLoop("Current", playerLoop); + RP1 = new AsyncReactiveProperty(999); + + + + RP1.Select(x => x * x).BindTo(text); + + //Update2().Forget(); //RunStandardDelayAsync().Forget(); @@ -342,17 +174,6 @@ public class SandboxMain : MonoBehaviour // UniTaskAsyncEnumerable.EveryUpdate(PlayerLoopTiming.FixedUpdate) - this.GetAsyncUpdateTrigger().ForEachAsync(_ => - { - UnityEngine.Debug.Log("Update Trigger 1"); - }).Forget(); - - - this.GetAsyncUpdateTrigger().ForEachAsync(_ => - { - UnityEngine.Debug.Log("Update Trigger 2"); - }).Forget(); - //await UniTask.Yield(PlayerLoopTiming.Update); //Debug.Log("Start:" + Time.frameCount); @@ -401,8 +222,8 @@ public class SandboxMain : MonoBehaviour //await okButton.GetAsyncClickEventHandler().DisableAutoClose() - // .Select((_, clickCount) => clickCount + 1) - // .FirstAsync(x => x == 5); + // .Select((_, clickCount) => clickCount + 1) + // .FirstAsync(x => x == 5); //Debug.Log("Click 5 times."); @@ -433,6 +254,9 @@ public class SandboxMain : MonoBehaviour okButton.onClick.AddListener(() => { // FooAsync().Forget(); + + RP1.Value += 3; + }); cancelButton.onClick.AddListener(() =>