# 1. Awaitable versus Coroutines and Task
Part of [[Async Code]]. Next: [[2. Awaitable Waits and Thread Switches]].
> [!note] Verification status
> Comparisons below follow the Unity 6000.5 Awaitable manual and the completion/continuation page. The pooling rule ("not safe to await multiple times") is official. **Code not compiled in Unity.**
## 1.1 Three ways to not block the main thread
| Model | What it is | Strength | Cost / catch |
|---|---|---|---|
| **Coroutine** (`IEnumerator` + `StartCoroutine`) | An iterator the PlayerLoop pumps each frame | Familiar, easy to `yield return WaitForSeconds`, cancelled when you `StopCoroutine` or destroy the behaviour (usually) | Awkward return values and try/catch; stack of nested enumerators; easy to leak if you start on a hidden object |
| **`.NET Task`** | The standard C# async type | Interops with any .NET library; Test Framework can return `Task` from a `[Test]` | SynchronizationContext posts continuations to the *next* `Update`, which adds latency at scale; more allocation; easy to forget that Unity APIs are main-thread only |
| **`Awaitable`** | Unity's pooled awaitable, used as `async Awaitable` / `async Awaitable<T>` | Same `await` syntax, cheaper than `Task`, resumes on the main thread if you started there (no "wait until next Update" tax by default) | **Pooled — do not await the same instance twice**; not a general .NET type; background work is not stopped automatically when you leave Play Mode |
The 6000.5 manual's position is that `Awaitable` is **usually preferable to `Task`** for asynchronous *Unity* code. Keep `Task` when you are talking to a .NET API that returns one, or when a test method must return `Task` ([[3. UnityTest Async Tests and Domain Reloads]]). Keep coroutines when you are extending a large existing coroutine codebase and a rewrite would not pay for itself this week — or when a Unity API still only offers a `YieldInstruction`.
Jobs + Burst ([[2. Jobs and Burst for GameObject Projects]]) remain the tool for *short, parallel, CPU-bound* work. `Awaitable.BackgroundThreadAsync` is for longer work you do not want on the main thread, not for 10,000 simple sums.
## 1.2 A first `async Awaitable` method
```csharp
using UnityEngine;
using UnityEngine.SceneManagement;
public sealed class BootLoader : MonoBehaviour
{
[SerializeField] string _firstScene = "Harbor";
async Awaitable Start()
{
await SceneManager.LoadSceneAsync(_firstScene);
await Awaitable.NextFrameAsync();
// First frame of the new scene has now run; safe to talk to objects that initialized in Start.
}
}
```
`LoadSceneAsync` returns an `AsyncOperation`. In Unity 6 you can `await` many `AsyncOperation`-style APIs directly, or wrap them with `Awaitable.FromAsyncOperation` (part 2) if your version or the API needs it. If `await sceneLoad` does not compile against a particular operation type, wrap it.
`async Awaitable Start()` (or `Awake`, button handlers, etc.) is legal. Be careful with **async void** — prefer `async Awaitable` plus `LogExceptionsAndForget()` (part 5) if you must fire and forget, rather than `async void` which is easy to lose exceptions from.
## 1.3 Continuations (why Awaitable exists)
When you `await` a `Task` that was started on the main thread, Unity's `UnitySynchronizationContext` typically resumes you on the **next Update**. That is correct (you are back on the main thread) and it is also a frame of latency, plus context-capture cost, if you do it a lot.
`Awaitable` continuations, unless documented otherwise:
- If the method was called from the **main thread**, it resumes on the main thread (without that "always next Update" tax as the reason for the type).
- Otherwise it resumes on a **thread-pool** thread.
Exceptions: `Awaitable.MainThreadAsync()` always resumes on the main thread; `Awaitable.BackgroundThreadAsync()` always resumes on a background thread. Those two are **local to the current method** — a callee that switches to the background does not permanently banish the *caller*. If the caller awaited from the main thread, the caller resumes on the main thread (official example in the continuation page).
This is the whole trick. You can hop to a background thread to crunch numbers, `return 42`, and the caller on the main thread can then call `SceneManager.LoadSceneAsync` safely.
## 1.4 Pooling: the rule you will break once
`Awaitable` instances are **pooled**. The official documentation is blunt: they are **not safe to `await` multiple times** in the same method (or at all). This is wrong:
```csharp
Awaitable wait = Awaitable.NextFrameAsync();
await wait;
await wait; // do not do this
```
Await the result of a call once, or call the helper again. Store a "logical operation" in your own field if you need to cancel it (`Cancel()`, part 3), but do not treat an `Awaitable` like a `Task` you can share with three subscribers.
## Official docs
- Awaitable manual: https://docs.unity.com/en-us/engine/6000.5/manual/scripting/optimization/async-await-support
- Continuations: https://docs.unity.com/en-us/engine/6000.5/manual/scripting/optimization/async-await-support/async-awaitable-continuations
- Scripting API: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/Awaitable.html