# 2. Awaitable Waits and Thread Switches Part of [[Async Code]]. Previous: [[1. Awaitable versus Coroutines and Task]]. Next: [[3. Cancellation Tokens]]. > [!note] Verification status > Static methods and the main-thread restriction on `WaitForSecondsAsync` are from the 6000.5 `Awaitable` Scripting API. Thread-switch behaviour is from the continuation page. **Code not compiled in Unity.** ## 2.1 The built-in waits | Method | Resumes | |---|---| | `Awaitable.NextFrameAsync(cancellationToken = default)` | On the next frame | | `Awaitable.EndOfFrameAsync(...)` | After all Unity subsystems have run for the current frame | | `Awaitable.FixedUpdateAsync(...)` | On the next fixed update | | `Awaitable.WaitForSecondsAsync(seconds, cancellationToken = default)` | After `seconds` (game time). **Main-thread only; always completes on the main thread.** | | `Awaitable.FromAsyncOperation(asyncOperation, ...)` | When a Unity `AsyncOperation` completes | All of the ones that take a `CancellationToken` throw `OperationCanceledException` if the token is canceled (part 3). That **aborts** the wait — you will not fall through to the next line unless you catch the exception. The `WaitForSecondsAsync` page is explicit about this: if you used cancellation as "the task finished early, continue," you must catch or you will skip the follow-up work. ```csharp using System.Threading; using UnityEngine; public sealed class DoorReveal : MonoBehaviour { public async Awaitable RevealAfterAsync(float delay, CancellationToken token) { await Awaitable.WaitForSecondsAsync(delay, token); // If token canceled during the wait, we never reach here. gameObject.SetActive(true); } } ``` `NextFrameAsync` is the replacement for `yield return null`. `FixedUpdateAsync` is the replacement for `WaitForFixedUpdate` when you are stepping physics ([[Physics 3D]]). Prefer `NextFrameAsync` plus a condition over a long `WaitForSecondsAsync` when you are waiting *for something to happen* (a panel to appear, a body to sleep). ## 2.2 Wrap an `AsyncOperation` ```csharp using UnityEngine; using UnityEngine.SceneManagement; public static class SceneAwait { public static async Awaitable LoadSingleAsync(string sceneName, System.Threading.CancellationToken token) { AsyncOperation op = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Single); if (op == null) return; // If your 6000.5 Scripting Reference shows a CancellationToken overload, pass token here. await Awaitable.FromAsyncOperation(op); } } ``` If a Unity API already returns something awaitable, use that. `FromAsyncOperation` is the adapter for APIs that still hand you a raw `AsyncOperation`. Addressables 2.9 handles are *not* `AsyncOperation`; use `handle.Task`, a coroutine `yield return handle`, or 4.x `ToAwaitable()` ([[3. Loading Instantiating and Releasing]]). ## 2.3 Switching threads ```csharp using UnityEngine; public sealed class HeightmapBakeNote : MonoBehaviour { public async Awaitable<float> ComputeAverageHeightAsync(float[] heights) { await Awaitable.BackgroundThreadAsync(); // We are on a thread-pool thread. Do not touch Unity objects, transforms, or GetComponent. double sum = 0; for (int i = 0; i < heights.Length; i++) sum += heights[i]; float average = heights.Length == 0 ? 0f : (float)(sum / heights.Length); // No need to hop back here if the *caller* awaited us from the main thread — // the official continuation rule resumes the caller on the main thread. return average; } public async Awaitable Start() { float[] sample = new float[1024]; float average = await ComputeAverageHeightAsync(sample); Debug.Log("Average height " + average); // main thread, safe } } ``` If you are on a background thread and need to call a Unity API *inside the same method*, `await Awaitable.MainThreadAsync()` first. That hop **waits until the next main-thread Update** if you are coming from the background — it is the expensive direction. Do as much as you can on the background, return a result, and let the caller (already on main) apply it. Rules that will save a lab: - No `GameObject`, `Transform`, `Renderer`, or `ScriptableObject` access off the main thread - Copy the data you need into arrays *before* you hop - Do not use `WaitForSecondsAsync` off the main thread (the API forbids it) For large grid work on terrain, prefer Jobs/Burst ([[2. Jobs and Burst for GameObject Projects]]) over a thread-pool loop. ## Official docs - Scripting API: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/Awaitable.html - WaitForSecondsAsync: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/Awaitable.WaitForSecondsAsync.html - Continuations: https://docs.unity.com/en-us/engine/6000.5/manual/scripting/optimization/async-await-support/async-awaitable-continuations