UniTask & Related things
Minh Khoa
Author
π¦ 1. UniTask what are they?
UniTask is a library async/await optimized for Unity, developed by Cysharp (the creator MemoryPack, MessagePipeβ¦).
It was created to completely replace:
IEnumerator+StartCoroutineasync Task(C# standard)- Old-style callbacks
π© 2. The problems with Unity before UniTask
β (1) Slow and hard-to-control coroutines
Coroutines have drawbacks:
- Does not catch exceptions
- Cannot be canceled properly
- Cannot await the result
- Does not run multithreaded
- Does not combine well with async/await
- No return value
- Cannot be reused (must recreate the enumerator β GC)
- Each yield creates boxing β GC spike
Unity essentially implements coroutines with mono scheduler β not optimized.
β (2) .NET async Task is too heavy for Unity
async Task creates a lot of allocation:
- Task object
- TaskCompletionSource
- Continuation delegate
- State machine
Unity GC is weak β easily causes freezes micro-stutter.
β (3) No await for Unity (await scene load, await animation, await frame)
Cannot write like:
await SceneManager.LoadSceneAsync("Game");
await UniTask.WaitForEndOfFrame();
await button.OnClickAsync();
Unity API completely has no awaitable.
π© 3. UniTask solves all the problems
β UniTask NO ALLOCATION
Unlike async Task, UniTask:
- does not create Task object
- does not create heap allocation
- uses struct-based awaiter
- runs extremely fast
This is why UniTask became the standard for Unity game performance.
β Await everything in Unity (the biggest strength)
UniTask adds a series of API that Unity does not have:
await UniTask.Delay(1000);
await UniTask.WaitForEndOfFrame();
await UniTask.WaitUntil(() => hp <= 0);
await transform.DOMove(...).ToUniTask();
await button.OnClickAsync();
await SceneManager.LoadSceneAsync("Battle").ToUniTask();
Clear, clean workflow, no callback hell.
β True multithreading (unlike coroutines)
Coroutines only run on the main thread.
UniTask can run on thread pool, worker threads:
await UniTask.Run(() => HeavyCalculation());
β Separate heavy logic from the main thread.
β CancellationToken β something coroutines do not have
Can cancel every async operation:
var cts = new CancellationTokenSource();
await DoSomethingAsync(cts.Token);
cts.Cancel();
Coroutines cannot be canceled that nicely.
β No need StartCoroutine
UniTask to use async/await:
await PlayerJumpAsync();
No need:
StartCoroutine(PlayerJump());
β Lighter than Task, stronger than Coroutine
=> UniTask = best of both worlds.
π© 4. If you don't have UniTask then what do you use?
There are 3 options, but they all FALL FAR SHORT UniTask.
β 1. Use plain Coroutine (traditional Unity)
Example:
StartCoroutine(LoadSceneRoutine());
Drawbacks:
- does not return a value
- cannot catch exceptions
- does not run on a background thread
- does not await async tasks
- not suitable for large systems
- hard to maintain
Only suitable for prototypes or small games.
β 2. Using C# async Task#
Example:
await Task.Delay(1000);
Drawbacks:
- extremely large ALLOCATION
- GC many
- heavy Task overhead
- No Unity await API
- Easily causes frame lag
Unity game production should not use async Task for continuous loops.
β 3. Using 3rd-party assets such as RSG Promises
Promise-based (JavaScript style), but:
- many GC
- hard to maintain
- API complicated
- cannot be unified with async/await
No longer popular.
π¦ 5. When SHOULD you use UniTask in real games?
β Loading screen (await scene load)
β Button waits for player (await OnClick)
β Wait for animation to finish
β Wait for spine animation to finish
β Wait for DOTween effects (await tween.ToUniTask())
β Wait for API network response
β Save data async
β Multiplayer async
β Camera transitions
β Audio fade
β Spawn sequence logic
UniTask helps make the code extremely clean:
Production code example:
await PlayIntroAsync();
await ShowPopupAsync();
await WaitForClickAsync();
await LoadStageAsync();
The flow is super clear and beautiful.
π¦ 6. So, why do almost every Unity studio use UniTask?
β Performance is not GC
β Very suitable for the async/await C#
β Native integration with Unity
β API very complete
β Can run on both the thread pool
β Use together with DOTween, Addressables, SceneManager, UI
Unity itself is also starting to move toward the model async/await for many systems β UniTask will live a long time.
π₯ 7. Strong conclusion
π― UniTask strong because:
- No allocation (zero GC)
- Optimized performance many times better than Task
- Full await support for Unity
- API extremely suitable for game workflow
- Has cancellation
- Has multithreading
- Integrates well with DOTween, Addressables, UI, loadingβ¦
π― If there is no UniTask then you will have to:
- Use Coroutine (weak, old, hard to maintain)
- Or use async Task (heavy, laggy, many GC)
- Or write callbacks yourself (ugly, hard to debug)