Under Construction
Unityβ€’β€’9 minβ€’176 viewsβ€’β€’

UniTask & Related things

Minh Khoa

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 + StartCoroutine
  • async 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

image.png=> 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)