# 4. Worked Examples - Scenes Delays and Loads Part of [[Async Code]]. Previous: [[3. Cancellation Tokens]]. Next: [[5. Async Pitfalls]]. > [!note] Verification status > Examples use official `Awaitable` waits and tokens. `SceneManager.LoadSceneAsync` is shown both directly awaited and wrapped in `FromAsyncOperation` — if a direct await fails to compile in your Editor, use the wrapper. Addressables 2.9 wrapping uses `handle.Task` (see [[3. Loading Instantiating and Releasing]]); `ToAwaitable()` is 4.x only. **Code not compiled in Unity.** ## 4.1 A telegraph that dies with the trap This is the Level Up! "hazard that telegraphs" exercise ([[Level Up! (Review)]], Level 12), written as `Awaitable` instead of a coroutine. ```csharp using System; using System.Threading; using UnityEngine; public sealed class TelegraphHazard : MonoBehaviour { [SerializeField] float _telegraphSeconds = 1f; [SerializeField] GameObject _warningVfx; [SerializeField] int _damage = 10; CancellationTokenSource _run; void OnEnable() { _run = CancellationTokenSource.CreateLinkedTokenSource( destroyCancellationToken, Application.exitCancellationToken); RunAsync(_run.Token).CatchCanceled(); } void OnDisable() { if (_run != null) { _run.Cancel(); _run.Dispose(); _run = null; } } async Awaitable RunAsync(CancellationToken token) { if (_warningVfx != null) _warningVfx.SetActive(true); try { await Awaitable.WaitForSecondsAsync(_telegraphSeconds, token); } finally { if (_warningVfx != null) _warningVfx.SetActive(false); } ApplyDamage(); } void ApplyDamage() { // Call into your combat code. This is the "fire" beat. Debug.Log("Hazard fired for " + _damage, this); } } static class AwaitableFireAndForget { // Helper for OnEnable: swallow cancel, log anything else. public static async void CatchCanceled(this Awaitable awaitable) { try { await awaitable; } catch (OperationCanceledException) { } catch (Exception ex) { Debug.LogException(ex); } } } ``` Notes for class: - `OnDisable` cancels so leaving a trigger (if you hook disable) or disabling the trap mid-telegraph does not fire damage later. - The `finally` hides the VFX even when canceled. - The `async void` helper is a teaching convenience. Prefer `LogExceptionsAndForget()` from the official API if it fits your case (part 5), and do not pile `async void` into production systems casually. ## 4.2 Additive scene load with a gate ```csharp using System; using System.Threading; using UnityEngine; using UnityEngine.SceneManagement; public sealed class LocationGate : MonoBehaviour { [SerializeField] string _sceneName = "Harbor"; public async Awaitable EnterAsync() { using var linked = CancellationTokenSource.CreateLinkedTokenSource( destroyCancellationToken, Application.exitCancellationToken); AsyncOperation op = SceneManager.LoadSceneAsync(_sceneName, LoadSceneMode.Additive); if (op == null) return; op.allowSceneActivation = false; while (op.progress < 0.9f) { linked.Token.ThrowIfCancellationRequested(); await Awaitable.NextFrameAsync(linked.Token); } op.allowSceneActivation = true; await Awaitable.FromAsyncOperation(op); await Awaitable.NextFrameAsync(linked.Token); Scene loaded = SceneManager.GetSceneByName(_sceneName); if (loaded.IsValid()) SceneManager.SetActiveScene(loaded); } } ``` `allowSceneActivation` and the 0.9 progress gate are long-standing `AsyncOperation` behaviour: Unity reports 0.9 until you allow activation. If that still holds in your 6000.5 Player (it has for years; **re-confirm if a load seems stuck at 90%**), this pattern lets you hold a loading screen on the last frame. Pair with [[SceneManagement in Unity 6]]. ## 4.3 Addressables, without inventing a 2.9 Awaitable API ```csharp using System.Threading; using System.Threading.Tasks; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; public sealed class HatLoader : MonoBehaviour { public string address = "cosmetics/hat-red"; public async Awaitable<GameObject> LoadPrefabAsync(CancellationToken token) { AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>(address); try { // 2.9: await the handle's Task. On Addressables 4.x you may use handle.ToAwaitable() instead. GameObject prefab = await handle.Task; token.ThrowIfCancellationRequested(); if (handle.Status != AsyncOperationStatus.Succeeded || prefab == null) throw new InvalidOperationException("Failed to load " + address); return prefab; } catch { if (handle.IsValid()) handle.Release(); throw; } } } ``` If the caller canceled while the handle was running, this still needs a `Release` — the `catch` does that. A production wrapper should also release on success when the caller is finished with the asset; ownership has to be documented (see [[3. Loading Instantiating and Releasing]]). ## Official docs - Awaitable API: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/Awaitable.html - [[SceneManagement in Unity 6]], [[Addressables]]