class Thread
package fivem.shared.core
Threads, waits and timers, wrapped around FiveM's Citizen scheduler.
FiveM has no preemption: a resource runs on a single Lua state, and a
"thread" is a coroutine that voluntarily yields. Everything here is built
on that model, so no locking or synchronisation is ever needed — but a
loop that forgets to wait will freeze the whole resource.
Thread.create(() -> {
while (true) {
Thread.wait(1000);
checkSomething();
}
});
var ticker = Thread.setInterval(500, () -> updateHud());
ticker.cancel();Static methods
staticadaptiveLoop(body:() ‑> Int):TimerHandle
Runs body repeatedly, waiting the number of milliseconds it returns
between runs. Returning a negative value stops the loop.
This is the pattern for distance-based polling, where you want to check often when the player is close and rarely when they're far:
Thread.adaptiveLoop(() -> {
var distance = LocalPlayer.coords().distance(shopCoords);
if (distance > 50) return 1000;
drawShopMarker();
return 0;
});staticinlinecreate(body:() ‑> Void):Void
Runs body on a new coroutine, starting on the next tick.
This is the FiveM equivalent of "go do this in the background" — the caller returns immediately.
staticinlinecreateNow(body:() ‑> Void):Void
Like create, but starts body immediately (synchronously, up to its
first wait) instead of deferring it to the next tick.
staticinlinedefer(body:() ‑> Void):Void
Runs body on a fresh coroutine on the next tick, without blocking the caller.
staticeveryFrame(body:() ‑> Void):TimerHandle
Runs body once per frame until cancelled.
Per-frame work is the most expensive thing a resource can do — reserve
this for drawing (markers, text, outlines), which genuinely has to
happen every frame, and use setInterval for everything else.
staticsetInterval(ms:Int, body:() ‑> Void):TimerHandle
Runs body every ms milliseconds until cancelled.
The delay is measured between runs, not on a fixed schedule: a body
that takes longer than ms delays the next run rather than stacking up.
staticsetTimeout(ms:Int, body:() ‑> Void):TimerHandle
Runs body once after ms milliseconds. Cancel via the returned handle
to prevent it from firing.
staticinlinewait(ms:Int = 0):Void
Yields for at least ms milliseconds. 0 waits for the next frame.
Must be called from inside a coroutine — a thread, event handler,
command handler or export. That covers nearly all resource code, but
not the top level of your main().
staticwaitUntil(condition:() ‑> Bool, timeoutMs:Int = 5000, pollMs:Int = 0):Bool
Blocks the current coroutine until condition returns true, polling
every pollMs. Returns true if the condition was met, false if
timeoutMs elapsed first.
Pass a timeoutMs of 0 to wait forever — but be aware that a condition
which never becomes true then leaks a coroutine for the resource's
lifetime.