From 740ca7ef0144fecbadecfb661ede6d53c6e24896 Mon Sep 17 00:00:00 2001 From: Yoshifumi Kawai <46207+neuecc@users.noreply.github.com> Date: Tue, 8 Oct 2024 23:02:26 +0900 Subject: [PATCH] Awaitable notes --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 2fe49de..1e3cfe4 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ async UniTask DemoAsync() await SceneManager.LoadSceneAsync("scene2"); // .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject + // after Unity 2022.2, you can use `destroyCancellationToken` in MonoBehaviour var asset2 = await Resources.LoadAsync("bar").WithCancellation(this.GetCancellationTokenOnDestroy()); // .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress @@ -293,6 +294,8 @@ public class MyBehaviour : MonoBehaviour } ``` +After Unity 2022.2, Unity adds CancellationToken in [MonoBehaviour.destroyCancellationToken](https://docs.unity3d.com/ScriptReference/MonoBehaviour-destroyCancellationToken.html) and [Application.exitCancellationToken](https://docs.unity3d.com/ScriptReference/Application-exitCancellationToken.html). + When cancellation is detected, all methods throw `OperationCanceledException` and propagate upstream. When exception(not limited to `OperationCanceledException`) is not handled in async method, it is propagated finally to `UniTaskScheduler.UnobservedTaskException`. The default behaviour of received unhandled exception is to write log as exception. Log level can be changed using `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to use custom behaviour, set an action to `UniTaskScheduler.UnobservedTaskException.` And also `OperationCanceledException` is a special exception, this is silently ignored at `UnobservedTaskException`. @@ -953,6 +956,14 @@ public class AsyncMessageBroker : IDisposable } ``` +vs Awaitable +--- +Unity 6 introduces the awaitable type, [Awaitable](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Awaitable.html). To put it simply, Awaitable can be considered a subset of UniTask, and in fact, Awaitable's design was influenced by UniTask. It should be able to handle PlayerLoop-based awaits, pooled Tasks, and support for cancellation with `CancellationToken` in a similar way. With its inclusion in the standard library, you may wonder whether to continue using UniTask or migrate to Awaitable. Here's a brief guide. + +First, the functionality provided by Awaitable is equivalent to what coroutines offer. Instead of `yield return`, you use await; `await NextFrameAsync()` replaces `yield return null`; and there are equivalents for `WaitForSeconds` and `EndOfFrame`. However, that's the extent of it. Being coroutine-based in terms of functionality, it lacks Task-based features. In practical application development using async/await, operations like `WhenAll` are essential. Additionally, UniTask enables many frame-based operations (such as `DelayFrame`) and more flexible PlayerLoopTiming control, which are not available in Awaitable. Of course, there's no Tracker Window either. + +Therefore, I recommend using UniTask for application development. UniTask is a superset of Awaitable and includes many essential features. For library development, where you want to avoid external dependencies, using Awaitable as a return type for methods would be appropriate. UniTask can be converted to Awaitable using `ToAwaitable`, and Awaitable can be converted to UniTask using `ToUniTask`, so there's no issue in handling Awaitable-based functionality within the UniTask library. Of course, if you don't need to worry about dependencies, using UniTask would be the best choice even for library development. + For Unit Testing --- Unity's `[UnityTest]` attribute can test coroutine(IEnumerator) but can not test async. `UniTask.ToCoroutine` bridges async/await to coroutine so you can test async methods.