| name | timers-and-async |
| description | Schedule and defer work in Unreal C++ — FTimerManager (SetTimer with FTimerHandle, looping and one-shot timers, SetTimerForNextTick, ClearTimer, PauseTimer/UnPauseTimer, timer delegates with payloads), async work (Async/EAsyncExecution, AsyncTask/ENamedThreads, TFuture/TPromise, FNonAbandonableTask/FAutoDeleteAsyncTask/FAsyncTask, FRunnable/FRunnableThread, the UE Tasks System UE::Tasks::Launch/FTask/FPipe), FTSTicker for non-actor ticking, thread-safety and game-thread marshaling, latent actions overview. Use when implementing a delay or repeating callback, replacing per-frame Tick with a periodic timer, deferring one frame, offloading CPU-heavy work to a background thread, or building a non-actor ticker. Cross-references actors-and-components (EndPlay cleanup) and delegates-and-events. |
| metadata | {"engine-version":"5.8","category":"gameplay-framework"} |
Timers & async work
Most "do this later / every N seconds" needs belong in a timer, not Tick. For
CPU-intensive work, move computation off the game thread using Async or the Tasks System,
then marshal results back — UObjects and actors must only be touched on the game thread.
When to use this skill
- A delay ("respawn in 3 s") or repeating callback ("regen every 0.5 s").
- Replacing a per-frame accumulator with a world-time-aware periodic timer.
- Deferring one frame (
SetTimerForNextTick), e.g. waiting for another actor to finish
spawning before continuing initialization.
- Offloading expensive work (procedural gen, parsing, path pre-computation) to a worker
thread and applying the result on the game thread.
- Ticking a non-actor subsystem without a
UActorComponent (FTSTicker).
Timers — FTimerManager
FTimerManager manages all gameplay timers for a UWorld. Actors reach it via
GetWorldTimerManager(); non-actor code uses GetWorld()->GetTimerManager() or the
global instance on UGameInstance.
Setting a timer
FTimerHandle RegenHandle;
GetWorldTimerManager().SetTimer(
RegenHandle,
this, &AMyHero::OnRegen,
0.5f,
true);
FTimerHandle RespawnHandle;
GetWorldTimerManager().SetTimer(
RespawnHandle,
this, &AMyHero::OnRespawn,
3.f,
false);
FTimerHandle WarmupHandle;
GetWorldTimerManager().SetTimer(
WarmupHandle,
[this]{ OnWarmupComplete(); },
1.f,
false,
2.f);
Keep FTimerHandle as a member so you can cancel or query the timer later. Calling
SetTimer on an already-valid handle cancels the old timer and starts a fresh one.
Next-tick scheduling
GetWorldTimerManager().SetTimerForNextTick(this, &AMyActor::AfterSpawn);
Querying and cancelling
GetWorldTimerManager().IsTimerActive(RegenHandle);
GetWorldTimerManager().GetTimerRemaining(RegenHandle);
GetWorldTimerManager().GetTimerElapsed(RegenHandle);
GetWorldTimerManager().PauseTimer(RegenHandle);
GetWorldTimerManager().UnPauseTimer(RegenHandle);
GetWorldTimerManager().ClearTimer(RegenHandle);
GetWorldTimerManager().ClearAllTimersForObject(this);
ClearTimer invalidates the handle. Passing a rate <= 0 to SetTimer is equivalent to
ClearTimer.
Mandatory EndPlay cleanup
virtual void EndPlay(const EEndPlayReason::Type Reason) override
{
Super::EndPlay(Reason);
GetWorldTimerManager().ClearTimer(RegenHandle);
GetWorldTimerManager().ClearTimer(RespawnHandle);
}
Clear every looping timer in EndPlay. A looping timer that outlives its bound object
will attempt to call a dangling pointer. EndPlay covers all exit reasons (destroy,
level unload, PIE end) — see actors-and-components.
How timers interact with game time
Timers advance on world time, so they automatically respect WorldSettings time
dilation, pausing (SetPause), and slow-motion. They do not fire more than once per
game frame even if the accumulated delta exceeds the rate (modulo bMaxOncePerFrame on
FTimerData). The game-thread-only note in the engine docs is accurate: FTimerManager
is not thread-safe; never set or clear timers from a background thread.
Full reference: references/timer-manager.md.
Async work off the game thread
Async / AsyncTask (quick lambdas)
#include "Async/Async.h"
Async(EAsyncExecution::ThreadPool, [Payload]()
{
const FResult R = ComputeHeavyResult(Payload);
AsyncTask(ENamedThreads::GameThread, [R]()
{
ApplyResult(R);
});
});
Async returns a TFuture<T> that lets callers poll or wait for the result. Prefer
EAsyncExecution::ThreadPool for short-to-medium work; use EAsyncExecution::Thread for
long-running work that must not block the pool.
TWeakObjectPtr<AMyActor> WeakSelf(this);
Async(EAsyncExecution::ThreadPool, [WeakSelf, Data]()
{
FResult R = DoWork(Data);
AsyncTask(ENamedThreads::GameThread, [WeakSelf, R]()
{
if (AMyActor* Self = WeakSelf.Get())
{
Self->ApplyResult(R);
}
});
});
Never capture a raw UObject* or AActor* for use on another thread — the object
can be garbage-collected while the lambda is in flight. Capture a TWeakObjectPtr and
call .Get() after you are back on the game thread.
FNonAbandonableTask / FAutoDeleteAsyncTask
For reusable, structured background tasks with their own data:
class FMyProcessTask : public FNonAbandonableTask
{
friend class FAutoDeleteAsyncTask<FMyProcessTask>;
TArray<FVector> Points;
explicit FMyProcessTask(TArray<FVector>&& InPoints)
: Points(MoveTemp(InPoints)) {}
void DoWork()
{
ProcessPoints(Points);
}
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FMyProcessTask, STATGROUP_ThreadPoolAsyncTasks);
}
};
(new FAutoDeleteAsyncTask<FMyProcessTask>(MoveTemp(SomePoints)))->StartBackgroundTask();
When you need to wait for completion or retrieve the result, use FAsyncTask<T> instead,
which exposes EnsureCompletion() and IsDone().
Full reference: references/async-and-tasks.md.
UE Tasks System (UE 5.1+, preferred for new code)
The modern Tasks System (UE::Tasks) builds on the same worker-thread backend as the
task graph but with a cleaner API, dependency graphs, and pipes.
#include "Tasks/Task.h"
using namespace UE::Tasks;
Launch(UE_SOURCE_LOCATION, []{ DoWork(); });
TTask<int32> Task = Launch(UE_SOURCE_LOCATION,
[]{ return ComputeValue(); });
int32 Val = Task.GetResult();
FTask A = Launch(UE_SOURCE_LOCATION, []{ StepOne(); });
FTask B = Launch(UE_SOURCE_LOCATION, []{ StepTwo(); }, A);
FPipe ResourcePipe{ TEXT("MyResourcePipe") };
FTask T = ResourcePipe.Launch(UE_SOURCE_LOCATION,
[this]{ Resource.Mutate(); });
Prefer this over raw Async/AsyncTask for new code that needs DAG-style dependencies
or serialized access to a shared resource.
Full reference: references/async-and-tasks.md.
FRunnable / FRunnableThread (long-running dedicated threads)
For long-running services (audio streaming, network I/O, simulation loops) that must own
a dedicated OS thread:
#include "HAL/Runnable.h"
#include "HAL/RunnableThread.h"
class FMyWorker : public FRunnable
{
public:
FMyWorker() : bStop(false) {}
virtual bool Init() override { return true; }
virtual uint32 Run() override
{
while (!bStop)
{
DoIterationWork();
FPlatformProcess::Sleep(0.01f);
}
return 0;
}
virtual void Stop() override { bStop = true; }
virtual void Exit() override {}
private:
TAtomic<bool> bStop;
};
FMyWorker* Worker = nullptr;
FRunnableThread* Thread = nullptr;
void AMyActor::BeginPlay
{
Super::();
Worker = ();
Thread = FRunnableThread::(Worker, ());
}
{
Super::(Reason);
(Thread) { Thread->( ); Thread; Thread = ; }
Worker; Worker = ;
}
Full reference: references/threads-and-runnables.md.
FTSTicker — non-actor periodic ticking
FTSTicker provides a periodic callback for subsystems and objects that do not have a
UActorComponent. It replaces the older FTicker (removed in UE5).
#include "Containers/Ticker.h"
FTSTicker::FDelegateHandle TickHandle =
FTSTicker::GetCoreTicker().AddTicker(
TEXT("MySubsystemTick"),
0.25f,
[this](float DeltaTime) -> bool
{
PollSubsystem(DeltaTime);
return true;
});
FTSTicker::RemoveTicker(TickHandle);
FTSTickerObjectBase is a convenience base class — subclass it and override Tick(float)
instead of managing the handle manually.
Full reference: references/tickers-and-latent.md.
Thread-safety rules (critical)
- Never read or write
UObject / AActor / UActorComponent state off the game
thread. This includes GetWorld(), spawning, delegate broadcast, and GC-tracked pointers.
- Capture copies of plain data (structs,
int32, float) into lambdas that cross
threads. Capture TWeakObjectPtr<T> for any UObject; validate with .Get() after
returning to the game thread.
- Protect mutable non-UObject state shared between threads with
FCriticalSection /
FScopeLock (HAL/CriticalSection.h, Misc/ScopeLock.h).
FTimerManager itself is game-thread-only; set/clear timers only from the game thread.
FTSTicker::AddTicker is thread-safe (the callback fires on the game thread); the
FTSTicker::RemoveTicker call blocks until any in-progress callback finishes.
Choosing the right mechanism
| Need | Mechanism |
|---|
| Delay or repeat at a fixed cadence | FTimerManager::SetTimer |
| Defer exactly one frame | SetTimerForNextTick |
| Per-frame smooth interpolation | Tick (enable selectively) |
| Short background work, fire-and-forget | Async(EAsyncExecution::ThreadPool, ...) |
| Background work with result / dependencies | UE::Tasks::Launch |
| Reusable background task class | FNonAbandonableTask + FAutoDeleteAsyncTask |
| Long-running dedicated OS thread | FRunnable + FRunnableThread |
| Non-actor periodic callback | FTSTicker |
Latent actions (Blueprint async nodes)
Blueprint Delay nodes and latent K2 functions are backed by FPendingLatentAction
(registered on UWorld::GetLatentActionManager()). From C++, prefer timers for actor
logic. If you need a Blueprint-exposed "async node" that shows a white execution pin,
subclass UBlueprintAsyncActionBase instead of implementing a raw FPendingLatentAction.
Gotchas
- Lost
FTimerHandle — a looping timer without a stored handle cannot be cancelled;
it runs until the world tears down.
- No EndPlay cleanup — a looping timer whose delegate references
this will call
into freed memory after the actor is destroyed; always clear in EndPlay.
- Raw
UObject* captured across threads — GC can collect the object while the lambda
is in flight; use TWeakObjectPtr and re-validate on the game thread.
- Timer rate
<= 0 — silently treated as ClearTimer; guard against accidental
zero rates when computing a dynamic interval.
SetTimerForNextTick has no handle — it cannot be cancelled; do not call it if the
actor might be destroyed before the next frame.
- Blocking the game thread on a future — calling
TFuture::Get() or FTask::Wait()
from the game thread stalls rendering; only block from worker/background threads or a
known safe point (e.g. level loading).
FTicker vs FTSTicker — FTicker was removed in UE5; always use FTSTicker.
Version notes
FTSTicker replaced FTicker in UE5. Any UE4-era code using FTicker::GetCoreTicker()
must be ported to FTSTicker::GetCoreTicker().
- The UE Tasks System (
UE::Tasks) was introduced in UE 5.0. Prefer it over direct
task-graph usage (TGraphTask) for new code in 5.8.
- Busy-waiting in
UE::Tasks was deprecated in UE 5.5 and replaced by oversubscription
(standby threads). Do not call the removed busy-wait APIs.
References & source material
Engine source (UE 5.8, under Engine/Source/):
Runtime/Engine/Classes/Engine/TimerHandle.h — FTimerHandle:11.
Runtime/Engine/Public/TimerManager.h — FTimerManager:137, SetTimer:167,
SetTimerForNextTick:249, ClearTimer:281, PauseTimer:304, UnPauseTimer:311,
IsTimerActive:331, GetTimerRemaining:444, ClearAllTimersForObject:291,
FTimerManagerTimerParameters:124.
Runtime/Core/Public/Async/Async.h — EAsyncExecution:27 (enum with TaskGraph,
Thread, ThreadPool, TaskGraphMainThread, TaskGraphMainTick), Async:299,
AsyncTask:463.
Runtime/Core/Public/Async/AsyncWork.h — FAutoDeleteAsyncTask:60,
FAsyncTaskBase:208, FAsyncTask:587, FNonAbandonableTask:666.
Runtime/Core/Public/Async/TaskGraphInterfaces.h — ENamedThreads:54 (namespace,
GameThread, AnyThread, RHIThread).
Runtime/Core/Public/Async/Future.h — TFuture:378, TPromise:527.
Runtime/Core/Public/Tasks/Task.h — UE::Tasks::TTask, FTask alias, Launch,
AddNested, Wait in namespace UE::Tasks.
Runtime/Core/Public/Tasks/Pipe.h — UE::Tasks::FPipe:28.
Runtime/Core/Public/HAL/Runnable.h — FRunnable:19 (Init, Run, Stop, Exit).
Runtime/Core/Public/HAL/RunnableThread.h — FRunnableThread:19, Create:44.
Runtime/Core/Public/Containers/Ticker.h — :26, :45,
:66, :136.
Official docs (UE 5.8, verified):
Deep-dive references in this skill:
- references/timer-manager.md — FTimerManager internals,
delegate variants, timer parameters struct, time-dilation interaction.
- references/async-and-tasks.md — Async/AsyncTask
patterns, TFuture/TPromise, FNonAbandonableTask, the UE Tasks System (Launch, FPipe,
prerequisites, task events).
- references/threads-and-runnables.md — FRunnable
lifecycle, FRunnableThread::Create, thread priorities, stopping safely.
- references/tickers-and-latent.md — FTSTicker API,
FTSTickerObjectBase pattern, latent actions overview.