# 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]]