Log inGet started

task

Updated October 20, 2026

The task namespace: 29 functions.

task/active

task.active() -> number

The number of live tasks this VM holds: waiting (task.wait), suspended (an await, or parked on a resumer) and deferred (task.defer, not yet run). The calling task itself is running and not counted. This is the count a new task is admitted against: task.spawn, task.spawnSystem and task.defer raise once it reaches 50000, and a task stops counting once it returns or is cancelled.

Returns number — Count of live tasks

task/await

task.await(promise_id) -> any

Yield until a promise settles and return its value; a promise that has already settled returns at once, with no yield. A rejected promise raises its error at the call (a string at the caller's line, any other error value as itself), and handle:rejection() reads why it was rejected, { message, origin?, stack? }; a rejection is kept apart from any value. The same call as the global await. Every coroutine that awaits a promise receives its result: the ones waiting when it settles and any that await it later, for as long as the engine keeps the promise, which is while a PromiseHandle naming it is alive, a coroutine waits on it, or its handle has left the VM as data and it has not been delivered yet. A PromiseHandle reads as its id string, and the id names the same promise, so hold the handle, not only its id, to await it later. A PromiseHandle is a future, a value to store and await later: handle:await() is this same call, handle:state() reads pending, resolved or rejected without taking the value, handle:rejection() reads why it was rejected, handle:source() names the line that created it, and @builtin::modules.future makes futures of your own; Future<T> types one whose await delivers a T. An id the engine keeps no promise by (one never created, or one already released), a second argument, or a value that is neither a handle nor an id raises an error at the call naming the reason.

Parameters

  • promise_id PromiseHandle | string — The PromiseHandle a promise-returning API handed back, or its id.

Returns any — The resolved value.

task/background

task.background() -> boolean

Declare that the calling task belongs in the background. A caller driving it inline (tool_exec, bash, execute) hands back the task's watchable handle on its next poll instead of waiting out an inline budget the work will outlast. Call it as soon as the operation knows its own scale, before the long stretch begins; the decision can be per-call, so a small scope stays inline and a large one is handed back. Returns false outside any scheduler task, where there is no handle to hand back. Completion still arrives the usual way: poll task.status(handle), read the watch file, or wait for the operation's own notice.

Returns boolean — True when the calling task was marked; false outside any task

See also: task/current · task/status

task/batched

task.batched(count, fn, batch_size?, on_complete?) -> number

Run fn(i) for i = 1..count, yielding to the next frame every batch_size iterations (default 100). Spawns a coroutine and returns its task handle immediately. Use for bulk spawn loops or mass mutations that would otherwise block the VM long enough to trip the watchdog.

Parameters

  • count number — Total number of iterations.
  • fn function — Function called as fn(i) for each iteration.
  • batch_size number (optional) — Iterations per frame before yielding (default 100).
  • on_complete function (optional) — Optional callback invoked after the final iteration.

Returns number — Task handle (the spawned coroutine).

task/bindCaller

task.bindCaller<F>(fn: F) -> F

A function that runs fn as the caller in scope now. A registered callback already runs as the caller that registered it; this carries the same guarantee through a dispatcher of your own. A library that registers one watcher or loop and calls several subscribers' functions from it binds each subscriber's function where the subscriber hands it over, so inside it task.caller() reads the subscriber, not whoever fires the dispatcher. The same rule as a registration decides what binds: a function written in world or user content, or a call made from content, binds the caller in scope and what a task started there belongs to (the component instance or scene load whose code is running, as task.spawn describes), so a task fn starts ends with that, whoever calls the bound function; engine library code binding its own function binds nothing, and neither does a call with no caller and nothing a task would belong to in scope, and fn then comes back unchanged. Binding happens where the subscriber hands its function over, in the same call: a function bound later binds whoever is in scope then. The bound function passes its arguments and results through. When fn raises, the bound function raises the same error value, without the traceback of the frame that raised it. When fn yields, it carries on as a task of its own under the bound caller, and the bound function returns no values at that first yield, so a dispatcher calling it moves on to the next subscriber. fn runs on a coroutine the engine keeps for the bound caller and reuses across that caller's bound calls, so coroutine.running() inside it can name the same thread on a later call; a call that yields keeps its coroutine as its task, and the next call runs on another.

Parameters

  • fn F — The function to run as the caller in scope now

Returns F — A function that calls fn as that caller, or fn itself when nothing binds

See also: task/caller

task/caller

task.caller() -> string?

The key of the caller the running code acts for (agent:<name>, client:<id>, actor:<sub>), or nil when no caller is in scope.

Returns string? — The current caller key, or nil when none is in scope

task/callerInfo

task.callerInfo(level?) -> string?

Return the formatted "<source>:<line>" of a Luau stack frame, or nil if the frame doesn't exist or has no source. level defaults to 1 (the immediate Lua caller); 2 skips one frame above, and so on. Used by task.loop to tag iteration-error warnings with the spawn site, and useful for any custom error-reporting wrapper since the stock debug.info is gated out of user scope. See gh#3416 / gh#3248.

Parameters

  • level number? — Stack level — 1 (default) = immediate Lua caller, 2 = caller's caller, etc.

Returns string? — "source:line" string, or nil when unavailable

task/cancel

task.cancel(handle, options?) -> (boolean, string?)

Cancel a running, waiting, suspended or deferred task, and task.status reports 'cancelled' from this call on. By default the task stops where it is suspended and is not resumed again, so a lock, an in-flight flag or a progress record it claimed stays claimed. From the cancel on, a coroutine.running() captured from it is refused by task.wake and coroutine.resume alike, each answering false and <call>: the task was cancelled (task.isCancellation recognises it), and from the end of the first scheduler tick after the cancel its coroutine is dead, ended with the cancellation error as its error. With { unwind = true } it unwinds instead: at the end of the first scheduler tick after the cancel, once that tick has resumed its tasks (the same frame for a cancel made from a component's update or editorUpdate, or from a task the tick resumes), it is resumed once, with a cancellation error raised at the call it is waiting in (task.wait, task.waitFrames, await, task.await or any other yield), so the code around that call runs its cleanup and the task then ends. The error passes through a pcall like any error, so local ok, err = pcall(function() task.wait(5) end) returns false and the code after it runs; task.isCancellation(err) recognises it. The unwind is that one resume: if the cleanup waits again, the task is stopped at that wait. A task started with task.spawnWith({ unwind = true }, fn), or spawned by such a task, unwinds whenever it is cancelled, by this call or by the end of what it belongs to (its component instance, its scene load), unless the cancel says { unwind = false }. A task.spawn or task.defer from a cleanup that is unwinding belongs to the cancelled task, so it is cancelled before it runs; work that has to outlive the task goes to task.spawnSystem. The unwinds of one frame share the execute budget: a cleanup that runs past it is halted and reported, and a task the frame reaches after the budget is spent is stopped without its unwind and reported. A task that is running when it is cancelled keeps running until its next wait, where its unwind is delivered. Returns true when this call cancelled the task. Returns false and the reason when there was nothing to cancel: the task already ended (finished, failed or cancelled), or no task has the handle. A handle names its task whichever VM owns it; a user VM cancels only the tasks it owns, and cancelling a live task another VM owns raises, naming that VM and leaving the task running. The trusted VM cancels any VM's task.

Parameters

  • handle number — Task handle from spawn/defer/delay
  • options { unwind?: boolean } (optional) — unwind = true resumes the task once with a cancellation error at its wait so its cleanup runs; unwind = false stops it without that resume; omitted, it unwinds when it was started to (task.spawnWith)

Returns boolean — True when this call cancelled a live task; false, with the reason as a second value, when there was nothing to cancel

task/collect

task.collect(handle?) -> boolean

Free a finished task handle (or all finished handles if no argument). Returns true if any were collected. The engine releases every finished task 300 frames (about 5 seconds at 60 FPS) after it settles, whoever spawned it; after that task.collect finds nothing to free, and task.status and task.result answer from the terminal history of the most recent 128 settled tasks.

Parameters

  • handle number? — Optional task handle to collect; omit to collect all

Returns boolean — Whether any handles were collected

task/current

task.current() -> number?

The handle of the task the calling code runs in, or nil outside any scheduler task. Pair it with task.status to tell whether the owner of a long-running operation is still being advanced. A task cancelled without unwinding stops mid-flight, so state it claimed (a lock, an in-flight flag, a progress record) outlives it; record task.current() when you take such a claim, and let the next caller reap it when task.status(owner) reports a terminal state. A task that unwinds when cancelled (task.cancel with { unwind = true }, or one started by task.spawnWith({ unwind = true }, fn)) runs the cleanup around its wait instead, and releases the claim itself. Scoped to the calling VM.

Returns number? — Handle of the calling task, or nil when not running inside one

task/defer

task.defer(fn, ...args) -> number

Schedule fn to run at the end of the current frame, passing it the values after fn as its arguments, nils included: task.defer(fn, 7) calls fn(7) then. Returns a task handle that can be used with task.status/task.cancel. A deferred task is live until it runs, and counts toward the same 50000 live tasks a VM holds as a spawned one does (task.active() reads the count); a defer past that raises, naming the count and the source holding the most.

Parameters

  • fn function — Function to run at end of frame
  • ... any (optional) — Values fn receives as its arguments

Returns number — Task handle

task/delay

task.delay(seconds, fn, ...args) -> number

Schedule fn to run after seconds of SIMULATION time, the clock engine.timeScale multiplies: at scale 0.25 the callback runs four wall-clock seconds later and at scale 0 it does not run at all. task.delayRealtime is the wall-clock form. fn receives the values after it as its arguments, nils included. Returns a task handle.

Parameters

  • seconds number — Seconds of simulation time before fn runs; engine.timeScale multiplies them. A finite number; NaN or an infinity raises an error at this call and spawns nothing.
  • fn function — Function to run after the delay.
  • ... any (optional) — Values fn receives as its arguments.

Returns number — Task handle for cancel/status.

task/delayRealtime

task.delayRealtime(seconds, fn, ...args) -> number

Schedule fn to run after seconds of REAL time, whatever engine.timeScale is set to. The wall-clock counterpart of task.delay, which counts simulation time. fn receives the values after it as its arguments, nils included. Returns a task handle.

Parameters

  • seconds number — Wall-clock seconds before fn runs. A finite number; NaN or an infinity raises an error at this call and spawns nothing.
  • fn function — Function to run after the delay.
  • ... any (optional) — Values fn receives as its arguments.

Returns number — Task handle for cancel/status.

task/delivered

task.delivered(handle) -> boolean

Whether a caller has already been handed this settled task's outcome: task.result returned its value or refused it naming the task failed or cancelled, or the wait tool answered with its result, its error or its cancellation. A host that delivers the outcome of a promoted call on its own (an agent's wake, a notification) asks this before it reads the outcome itself, so an outcome the caller already took is not delivered again. Kept on the task's terminal record, so it answers for as long as task.status does; false for a task still running, one nothing has read the outcome of, or a handle unknown.

Parameters

  • handle number — Task handle from spawn/defer/delay, or the taskId of a promoted call

Returns boolean — True once a caller has been handed the outcome

See also: task/result · task/status

task/inflight

task.inflight(label) -> number

Number of scheduler tasks with the given label that have been dispatched and not yet reached a terminal state (finished or cancelled). Component lifecycle hooks dispatch under their hook name — task.inflight("awake") == 0 means every awake() coroutine has settled. The join primitive for "loaded means loaded": scene-load completion polls it so 'done' includes the detached lifecycle work the spawn dispatched, not merely the entity rows.

Parameters

  • label string — Task label to count (e.g. "awake")

Returns number — Count of in-flight tasks carrying that label

task/isCancellation

task.isCancellation(err) -> boolean

Whether err is a cancellation: the error a cancelled task's unwind raises at its wait (see task.cancel and its unwind option), or the error an await of a cancelled future raises (f:cancel()), as caught by a pcall or raised again with error(err). It also recognises the refusal task.wake and coroutine.resume answer for the coroutine of a cancelled task (task.wake: the task was cancelled), which is how code resuming a captured coroutine, a signal's waiter, tells a waiter the cancel ended from one that failed. Use it where a pcall around a wait has to tell a cancel apart from the waited-on work failing: local ok, err = pcall(function() await(p) end); if not ok and task.isCancellation(err) then release() end.

Parameters

  • err any — An error value, as a pcall returns it

Returns boolean — True when err is a cancellation error, or a resume's refusal of a cancelled task's coroutine

task/loop

task.loop(fn, dt?) -> number

Spawn a self-healing per-frame loop. Each iteration runs fn(delta) where delta is the seconds elapsed since the previous iteration, counted on the same SIMULATION clock task.wait counts and engine.timeScale multiplies — so task.loop(function(delta) pos += speed * delta end) moves in step with the physics and the update(dt) beside it, and slows, speeds and freezes with them; a no-argument function() end still works. An uncaught error inside fn is caught, surfaced to task.onError (if registered), logged via log.warn, and the loop continues on the next iteration. Use this for the common scene pattern of one long-lived coroutine that drives input/sim/UI per frame; a bad nil-deref on one entity no longer kills the entire loop. See gh#3248.

Parameters

  • fn function — Body to run every iteration, called as fn(delta) with the simulation seconds elapsed since the previous iteration. Errors are caught — they do not kill the loop.
  • dt number (optional) — Seconds of simulation time to wait between iterations (default 0 = next frame); engine.timeScale multiplies them. This is the loop interval, distinct from the per-iteration delta passed to fn. A finite number; NaN or an infinity raises an error at this call and starts no loop.

Returns number — Task handle (cancel via task.cancel to stop the loop).

task/onError

task.onError(handler: ((err: string, source: string?, handle: number) -> ())?)

Register a per-VM error handler invoked when a coroutine spawned via task.spawn / task.defer / task.delay errors. The handler receives (err_message, source_name, handle) and is itself called under pcall — its own errors are caught and logged, never re-raised. Passing nil clears the handler. Use this to surface 'silent freeze' coroutine deaths to scene-side code (e.g. show a SCRIPT ERROR overlay, restart the loop with backoff, escalate to telemetry). See gh#3248.

Parameters

  • handler function | nil — Function called as (err, source, handle) on coroutine error, or nil to clear

task/promises

task.promises() -> {entries: number, settled: number, pending: number, abandoned: number, pinned: number, handles: number}

What the engine keeps for promises, across every VM. entries is the number of promises it holds any record for: settled hold a result it keeps for awaiters, pending have not settled. A promise is kept while something can still await it: while a PromiseHandle naming it is alive, while a coroutine waits on it, or, once its handle has left its VM as data (a return value, a message, JSON), until it is awaited (pinned). abandoned counts pending promises whose every handle was collected; their results are dropped when they arrive. handles counts the live PromiseHandle objects. A handle the collector took is released on the next frame.

Returns {entries: number, settled: number, pending: number, abandoned: number, pinned: number, handles: number} — Promise record counts

See also: task/active

task/result

task.result(handle) -> any?

Get a FINISHED task's return value, decoded from its lossless JSON capture. Raises when the task is not finished — still running/waiting/deferred, failed, cancelled, or an unknown handle — naming the state, so a nil return means exactly one thing: the task finished and returned nil. Poll task.status(handle) until it reports 'finished' (or wait on the watch file a promoted call gives you), then read the result.

Parameters

  • handle number — Task handle from spawn/defer/delay

Returns any? — The finished task's return value; nil only when it returned nil. Raises otherwise.

task/spawn

task.spawn(fn, ...args) -> number

Spawn a new coroutine that runs fn, passing it the values after fn as its arguments, nils included: task.spawn(fn, 1, nil, "x") calls fn(1, nil, "x"). fn runs at once, up to its first yield. Returns a numeric task handle (not a thread) that can be passed to task.cancel/task.status. A VM holds at most 50000 live tasks (waiting, suspended or deferred; task.active() reads the count), and a spawn past that raises, naming the count and the source holding the most. Where the spawn happens decides when the task runs. From a component hook other than editorUpdate (awake, start, onEnable, update, fixedUpdate, onCallback and the rest) or a gameplay task it is a gameplay task: it runs while gameplay runs and is held while engine.paused is set, which is how edit mode starts, so a task spawned from awake in the editor waits until engine.paused = false. From editorUpdate (or an editor task) it is an editor task: it runs on the frames the editor ticks, edit mode and play while paused, and is held while play runs. From anywhere else (a module, an entrypoint, execute, a system task) it always runs; task.spawnSystem makes a task that always runs from a component too. A held task reads held_by in its /runtime/tasks record, and a gameplay task that waits while paused is reported once per component and line in the log. The task belongs to what started it and is cancelled when that ends: a task started by a component instance's own code (from one of its hooks, onCallback included, from its methods and the functions it defines, whichever code calls them, or from a callback it registered, whichever code fires the callback) is cancelled when the instance is destroyed, hot-reloaded, or torn down with the play world, whatever code added the component; a task started by a scene's entrypoint (its body, onLoad, update, localPlayerReady, playerJoined, playerLeft, onCallback, a callback it registered, or a component method it calls) is cancelled when that load of the layer ends, by layers.reload, layers.unload, a scene swap or a mode flip. A task started in a task scope (scopes.run, scopes.open), a component method called there included, belongs to the scope, and is cancelled when the scope ends. A task spawned by another task belongs to what that one belongs to. A task a module body spawns from its own code, a task.spawnSystem task, and a task an execute call starts outside those end only by returning or by task.cancel; the last is listed by scopes.list() under its call's exec: scope, and scopes.release of that scope cancels it. What ends a task is listed as owners in /runtime/tasks//summary.json while it lives, and a cancelled task's record, read at /runtime/tasks/any//summary.json, names what cancelled it as cancelled_by (task.cancel, component: hot-reload, component: destroy, component: replace, component: release, or ended); the end of an instance of a world's own component that cancels tasks also posts an info event naming the component, the instance, the entity, which end it was, how many tasks ended and their handles, delivered with the next tool call's response.

Parameters

  • fn function — Function to run in a new coroutine
  • ... any (optional) — Values fn receives as its arguments

Returns number — Task handle (numeric ID) for cancel/status

task/spawnSystem

task.spawnSystem(fn, ...args) -> number

Like task.spawn, fn receiving the values after it as its arguments, but the coroutine is a system task wherever it is started: it runs whatever the pause and mode, even when started from a component's awake()/onEnable(). It also outlives what started it: ending the component or the scene load that spawned it does not cancel it, so it ends only by returning or by task.cancel. Use for system-level loops (input ticking, watchers, editor tooling, a server or device link) that must run regardless of pause and of the code that launched them. A task.spawn inside it is a system task too.

Parameters

  • fn function — Function to run in a new system-owned coroutine
  • ... any (optional) — Values fn receives as its arguments

Returns number — Task handle (numeric ID) for cancel/status

task/spawnWith

task.spawnWith(options, fn, ...args) -> number

task.spawn(fn, ...args), started as options says. { unwind = true } starts a task that unwinds whenever it is cancelled: by task.cancel(handle), or by the end of what it belongs to (its component instance's destroy or hot-reload, its scene load's unload). Unwinding, it is resumed once with a cancellation error raised at the call it is waiting in, so a pcall or cleanup around that wait runs before the task ends (see task.cancel). Each task it spawns unwinds too, unless its own start says { unwind = false }, so work started under one such task releases what it claimed however it is ended. Everything else about the task is as task.spawn states it: the class it runs as, what it belongs to, and its handle.

Parameters

  • options { unwind?: boolean } — How the task starts: unwind says whether it unwinds when cancelled
  • fn function — Function to run in a new coroutine
  • ... any (optional) — Values fn receives as its arguments

Returns number — Task handle (numeric ID) for cancel/status

task/status

task.status(handle) -> string

Get task status: 'running' (executing), 'waiting' (task.wait), 'suspended' (await), 'deferred' (queued), 'finished' (completed), 'failed' (raised an error), 'cancelled', or 'dead' (unknown handle / aged out of terminal history). The first four are live and the last four are terminal: a task reads live on every poll until it settles, and once terminal it never reads live again. A handle names its task whichever VM owns it, so a task the trusted VM runs (a promoted tool call, for one) reads the same here as it does under /runtime/tasks. A finished task that was auto-collected still reports its terminal status from the terminal-history cache (mirrors task.result), so missing the live finished flip doesn't lose the outcome. What a 'waiting' task waits for is in its record, /runtime/tasks/any/<handle>/summary.json: wait names the deadline, the clock it counts on and whether that clock runs, so a wait frozen by engine.timeScale = 0 or a pause reads clock_state = "paused".

Parameters

  • handle number — Task handle from spawn/defer/delay

Returns string — Task status: 'running' | 'waiting' | 'suspended' | 'deferred' | 'finished' | 'failed' | 'cancelled' | 'dead'

task/wait

task.wait(seconds?) -> number

Yield the current coroutine. With a duration, resumes after seconds of SIMULATION time: the clock engine.timeScale multiplies. At scale 0.25 a task.wait(1) takes four seconds of wall clock, at scale 4 a quarter of one, and at scale 0 it never comes due; the same holds while gameplay is paused, for a wait a component started. That is the clock a timed sequence wants, because it keeps step with the motion, the physics and the update(dt) beside it. task.waitRealtime(seconds) is the wall-clock form, for a timeout, an interface transition or a test. With no duration: task.wait(): resumes on the NEXT frame: time.frameCount() reads exactly one higher on the other side of it, wherever in the frame the wait was armed. It returns the seconds that passed on the clock it counted, so the return of a scaled wait is the scaled figure; time.realtime() either side of it measures the wall clock. The engine steps whether or not a tool call is in flight, so a wait armed in one call is measured against a world that kept running through the real seconds before the next one; the core/engine guide's "How fast time passes" states which states run the gameplay clock.

Parameters

  • seconds number (optional) — Seconds of simulation time to wait; engine.timeScale multiplies them. Omit to wait exactly one frame. A finite number: zero or less waits exactly one frame, and NaN or an infinity raises an error naming the value.

Returns number — Seconds elapsed on the clock the wait counted — simulation seconds, so a scaled wait answers the scaled figure.

task/waitDrawnFrames

task.waitDrawnFrames(frames?, opts?) -> number

Yield the current coroutine until the renderer has DRAWN frames more frames (default: 1), and ask it to keep drawing for as long as the wait lasts. This is the wait to use before reading anything the renderer produced, because the engine runs a frame whether or not anything wants a picture of it, and a headless renderer declines the ones nothing is consuming. A frame drawn while a pipeline build the scene asked for is still landing off the frame thread is not one of them, since its draws bound the placeholder, so a material created a moment ago is drawn with its own shader by the time this returns: the settled wait is the one for a picture (a capture, a pixel readback). task.waitFrames(n) counts frames the ENGINE ran, which on an idle headless engine is many times the number it drew, and time.drawnFrameCount() counts every frame the renderer drew, the placeholder ones among them, so it moves while this wait is still running. The wait is bounded on those builds, and each bound is counted in engine frames and in seconds together and spent once both are gone, so a slower host is given the same frames and a host whose frames cost nothing is given the same seconds. It holds for as long as the count of builds in flight keeps moving, and RAISES an error naming that count once it stands still for 300 engine frames and 5 seconds with no build being compiled, once the wait has spent 1800 engine frames and 30 seconds behind builds that kept arriving or landing, whether or not one is being compiled at that moment, or once it has waited 600 seconds while a build a worker began is still being compiled. { settled = false } counts every frame the renderer draws instead, the placeholder ones among them, and is bounded on nothing but the renderer drawing: the wait for a quantity each drawn frame republishes whatever pipelines it bound, such as camera.observe(), so a compile burst elsewhere in the scene holds it no longer than a frame takes to draw. A caller that would rather have the frame the renderer did draw once a bound is spent takes the refusal with pcall and falls back to task.waitFrames.

Parameters

  • frames number (optional) — Drawn frames to wait for. Default 1. A finite count no greater than 2^53: a count below 1 waits one frame, and NaN, an infinity or a larger count raises an error naming the value.
  • opts { settled: boolean? } (optional) — settled (default true): count only the frames drawn with every pipeline the scene asked for in place. false counts every frame the renderer draws. Anything but a table, a key other than settled (a misspelling such as setled among them), or a settled that is not a boolean, raises an error naming what was passed.

Returns number — Frames the renderer drew across the wait.

task/waitFrames

task.waitFrames(frames?) -> number

Yield the current coroutine until frames more frames have run (default: 1). Exactly that many: the deadline is the engine's own frame counter, the one time.frameCount() reads, so local a = time.frameCount() task.waitFrames(n) leaves time.frameCount() - a == n however long those frames took and wherever in the frame the wait was armed. Work that lands a fixed number of frames later is read by counting frames rather than seconds, and one yield covers the whole span, where the same wait spelled as repeated task.wait() calls costs a scheduler round-trip per frame. It counts frames the ENGINE ran; frames the renderer submitted is a separate count (renderer.framePacing().submittedFrames), lower whenever the renderer declines a frame nothing is consuming.

Parameters

  • frames number (optional) — Frames to wait. Default 1 (the next frame). A finite count no greater than 2^53: a count below 1 waits one frame, and NaN, an infinity or a larger count raises an error naming the value.

Returns number — Frames waited.

task/waitRealtime

task.waitRealtime(seconds?) -> number

Yield the current coroutine for seconds of REAL time — the engine's monotonic clock, the one time.realtime() reads. engine.timeScale does not reach it, so this is the same number of wall-clock seconds at every scale and the wait to use for a timeout, an interface transition, a retry backoff or a test that has to finish. task.wait(seconds) counts simulation time instead, which is what a timed gameplay sequence wants. A wait a component started is still held while gameplay is paused — its continuation is gameplay logic — but its deadline goes on running through the pause, so it comes due on the first frame after it. With no duration it resumes on the next frame, as task.wait() does.

Parameters

  • seconds number (optional) — Wall-clock seconds to wait. Omit to wait exactly one frame. A finite number: zero or less waits exactly one frame, and NaN or an infinity raises an error naming the value.

Returns number — Wall-clock seconds elapsed.

task/wake

task.wake(co: thread, ...: any) -> (boolean, string?)

Resume a suspended coroutine and keep the scheduler's ownership of what it yields next. Code that captures coroutine.running() and resumes it later (a signal, a component event) uses this so the resumed coroutine's task.wait still reaches the scheduler and its task keeps reporting status and result. The wait the task is suspended in (task.wait, task.waitRealtime, task.waitFrames, task.await or a parked yield) ends with this resume, and the next wait it enters lasts as long as it names. A coroutine the scheduler holds no task for is resumed directly, and one whose task was cancelled is refused with task.wake: the task was cancelled, which task.isCancellation recognises. A coroutine a debugger breakpoint or step holds is refused with task.wake: the debugger holds the coroutine until a debugger continue or step has run it on. Returns true when the resume ran, or false and the reason it could not.

Parameters

  • co thread — The suspended coroutine to resume
  • ... any (optional) — Values the coroutine's yield returns

Returns boolean — Whether the coroutine was resumed

  • api
  • reference