UniTask & Related Topics
Minh Khoa
Author
## π¦ 1. UniTask what is it?
UniTask is the library Unity-optimized async/awaitdeveloped 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, hard-to-control coroutines
Coroutines have drawbacks:
- Do not catch exceptions
- Cannot be canceled properly
- Cannot await results
- Do not run multithreaded
- Do not combine well with async/await
- No return value
- Not reusable (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 weak β easily causes freezes micro-stutter.
β (3) No await for Unity (await scene load, await animation, await frame)
Cannot write like this:
await SceneManager.LoadSceneAsync("Game");
await UniTask.WaitForEndOfFrame();
await button.OnClickAsync();
Unity API completely has no awaitable.
π© 3. UniTask solve all problems
β UniTask NO ALLOCATION
Unlike async Task, UniTask:
- does not create Task objects
- 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 (strongest point)
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.
β Real multithreading (unlike coroutines)
Coroutines only run on the main thread.
UniTask can run on thread poolworker 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
Coroutine
- Can be used directly with Unity API
- Cannot be awaited
- No direct return value
- No proper cancel support
- Does not support multithreading
- Has GC allocation
async Task
- Can be awaited
- Has a return value
- Supports cancel
- Can run multithreaded
- Does not use Unity API directly on a worker thread
- Still has GC allocation
UniTask
- Creates almost no GC
- Can be awaited
- Has a return value
- Supports cancel
- Can be multithreaded
- Works well with Unity API
- Specifically optimized for Unity
π© 4. If there isn't UniTask what do you use?
There are 3 options, but they are ALL FAR WORSE UniTask.
β 1. Use pure Coroutine (traditional Unity)
Example:
StartCoroutine(LoadSceneRoutine());
Disadvantages:
- no return value
- cannot catch exceptions
- does not run on a background thread
- cannot await async tasks
- not suitable for large systems
- hard to maintain
Only usable for prototypes or small games.
β 2. Use C# async Task#
Example:
await Task.Delay(1000);
Disadvantages:
- EXTREMELY large ALLOCATION
- GC many
- heavy Task overhead
- No Unity await API
- Easy to cause frame lag
Unity game production should not use async Task for continuous loops.
β 3. Use 3rd-party assets like RSG Promises
Promise-based (JavaScript style), but:
- many GC
- hard to maintain
- API complex
- cannot be unified with async/await
No longer common.
π¦ 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 the code stay 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 architecture async/await C#
β Native integration with Unity
β API extremely complete
β Runs on the thread pool as well
β Use together with DOTween, Addressables, SceneManager, UI
Unity itself is also starting to move to the model async/await for many systems β UniTask will have long-term viability.
π₯ 7. Strong conclusion
π― UniTask strong because:
- No allocation (zero GC)
- Much better performance optimization than Task
- Full await support for Unity
- API very well suited to 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)