# 3. Cancellation Tokens Part of [[Async Code]]. Previous: [[2. Awaitable Waits and Thread Switches]]. Next: [[4. Worked Examples - Scenes Delays and Loads]]. > [!note] Verification status > `destroyCancellationToken` is on the 6000.5 `MonoBehaviour` Scripting API. `Application.exitCancellationToken` is documented on the `Application` Scripting API (raised when Play Mode exits in the Editor, or when the application quits). `Awaitable.Cancel()` throws `OperationCanceledException` to the awaiter. `WaitForSecondsAsync` / `NextFrameAsync` honor a supplied token the same way. **Code not compiled in Unity.** ## 3.1 Why this is not optional A coroutine started with `StartCoroutine` on a `MonoBehaviour` usually stops when the behaviour is destroyed or the object is disabled (with caveats). An `async Awaitable` method is **just a state machine**. If the GameObject dies mid-await, the rest of the method will still try to run unless you cancel. The first line after the await will then touch a destroyed object, or worse, keep going after you have left Play Mode. Unity gives you two official tokens; you should pass one (or a link of both) into almost every wait. ## 3.2 `destroyCancellationToken` Every `MonoBehaviour` has `destroyCancellationToken`. It is canceled when that behaviour is destroyed. Cache it only if you understand it will still represent *this* object's destruction — the 6000.5 docs say it is raised when the behaviour is destroyed. ```csharp using System; using System.Threading; using UnityEngine; public sealed class PulseScale : MonoBehaviour { public async Awaitable Start() { try { while (true) { await Awaitable.WaitForSecondsAsync(0.25f, destroyCancellationToken); transform.localScale = Vector3.one * 1.1f; await Awaitable.WaitForSecondsAsync(0.25f, destroyCancellationToken); transform.localScale = Vector3.one; } } catch (OperationCanceledException) { // Destroyed mid-pulse. This is a normal exit, not an error. } } } ``` Do not log this exception as an error in `Update` loops. It is the happy path for "the object went away." ## 3.3 `Application.exitCancellationToken` Unity **does not automatically stop background work** when you leave Play Mode. Official guidance: use `Application.exitCancellationToken` to cancel work at Play Mode exit (Editor) or at application quit (Player). Any `async` method that calls `BackgroundThreadAsync`, or that might still be running when you press Stop, should observe this token. A handy pattern is a *linked* token that dies if **either** the object is destroyed **or** Play Mode ends: ```csharp using System; using System.Threading; using UnityEngine; public sealed class LinkedWait : MonoBehaviour { public async Awaitable RunAsync() { using var linked = CancellationTokenSource.CreateLinkedTokenSource( destroyCancellationToken, Application.exitCancellationToken); try { await Awaitable.WaitForSecondsAsync(5f, linked.Token); Debug.Log("Five seconds elapsed, still alive, still in play."); } catch (OperationCanceledException) { // Destroyed, or leaving Play Mode / quitting. } } } ``` `using` disposes the linked source. Do not forget that; leaked token sources are still leaks. ## 3.4 `Awaitable.Cancel()` You can also cancel a specific awaitable: ```csharp Awaitable wait = Awaitable.WaitForSecondsAsync(10f); // later, perhaps from OnDisable: wait.Cancel(); ``` The awaiter gets `OperationCanceledException`. The Scripting API notes that this is equivalent, for methods that also take a token, to canceling that token. Prefer passing a token you own — it can cancel *all* waits in a sequence, not just one stored handle. ## 3.5 Don't use cancel to mean "success" The `WaitForSecondsAsync` documentation warns: if you cancel to say "we arrived early, now do the next thing," the exception **skips** the next thing unless you catch it. Structure it like this: ```csharp try { await Awaitable.WaitForSecondsAsync(telegraphSeconds, token); } catch (OperationCanceledException) { if (token.IsCancellationRequested) return; // genuine abort throw; } ApplyHazard(); ``` If "player already left the trigger, skip the hazard" is a success path, use a `bool` you check after a completed wait, not a canceled token, or catch and branch with care. ## Official docs - destroyCancellationToken: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/MonoBehaviour-destroyCancellationToken.html - exitCancellationToken: https://docs.unity3d.com/ScriptReference/Application-exitCancellationToken.html - Cancel: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/Awaitable.Cancel.html - WaitForSecondsAsync: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/Awaitable.WaitForSecondsAsync.html - Continuations (exitCancellationToken mention): https://docs.unity.com/en-us/engine/6000.5/manual/scripting/optimization/async-await-support/async-awaitable-continuations