Tasks & Yielding
A script can pause and resume later without blocking the game. This is called yielding: the script hands control back to the engine, and the engine resumes it when the thing it’s waiting on is ready. It’s what lets you write a sequence top-to-bottom instead of scattering it across callbacks:
function Script:OnReady() Wait(1) -- pause for a second local door = Asset.LoadAwait("/Game/Props/Door") -- pause until it loads self:Spawn(door) print("ready")endWhile this script is paused, every other script keeps running — only this one is suspended.
Wait(seconds) pauses the current script for seconds, then resumes it. Wait()
with no argument resumes on the next frame.
Wait(0.5) -- half a secondTask.Wait(0.5) -- identical; the Task.* spellingWhere you can yield
Section titled “Where you can yield”Yielding only works from a script thread that is allowed to pause. The
once/per-event hooks run on such a thread, so you can Wait (and await) directly
inside them. The per-frame and teardown hooks do not — a hook that ran every
frame can’t pause mid-frame.
| Hook | Can yield directly? |
|---|---|
OnAttach, OnReady | Yes |
OnInput, OnContactBegin/End, OnOverlapBegin/End | Yes |
OnUpdate, OnFixedUpdate, OnEditorUpdate | No — use Task.Spawn |
OnDetach | No (the entity is going away) |
From a hook that can’t yield, start a task: a separate script thread that can.
function Script:OnUpdate(dt) if self.ShouldOpen and not self.Opening then self.Opening = true Task.Spawn(function() Wait(0.25) self:Open() -- OnUpdate already returned; this runs on its own thread end) endendThe Task library
Section titled “The Task library”| Call | Does |
|---|---|
Task.Wait(seconds) | Pause the current thread, then resume. Same as Wait. |
Task.Spawn(fn, ...) | Run fn now on a new thread (passing any extra args), up to its first pause. |
Task.Delay(seconds, fn) | Run fn on a new thread after seconds. |
Task.Defer(fn) | Run fn on a new thread next frame. |
Task.Delay and Task.Defer take a function of no arguments — capture what it
needs with a closure (Task.Delay(2, function() self:Explode() end)).
Awaitable loading
Section titled “Awaitable loading”Asset.LoadAwait(path) pauses until the asset finishes loading in the
background, then resumes with it (or nil if the path doesn’t resolve). It’s the
pausing form of Asset.LoadAsync — no callback,
just a return value:
Task.Spawn(function() local fx = Asset.LoadAwait("/Game/FX/Explosion") if fx then self:Spawn(fx) endend)Cancellation
Section titled “Cancellation”Waits are tied to your entity. If the entity is destroyed while a script is paused, the pending wait is dropped — the thread is simply never resumed, so it can’t run code against a dead entity. You don’t need to clean these up by hand.