menu_bookDocumentation

Async Components: Coroutine-Style Async Tasks on the Main Thread

calendar_today May 20, 2026 schedule ~1 min read person PatrickJr verified 50

Async tasks in s&box run on the main thread by default, operating like coroutines. They are the recommended replacement for coroutines in s&box.

Basic Async

CSHARP
async Task PrintSomething( float waitSeconds, string message )
{
    await Task.DelaySeconds( waitSeconds );
    Log.Info( message );
}

Frame-by-Frame Lerping

Use Task.Frame() to wait one frame, enabling smooth animations:

CSHARP
async Task LerpSize( float seconds, Vector3 to, Easing.Function easer )
{
    TimeSince timeSince = 0;
    Vector3 from = WorldScale;

    while ( timeSince < seconds )
    {
        var size = Vector3.Lerp( from, to, easer( timeSince / seconds ) );
        WorldScale = size;
        await Task.Frame();
    }
}

await LerpSize( 3.0f, Vector3.One * 3.3f, Easing.BounceOut );

Multiple Concurrent Tasks

CSHARP
async Task DoMultipleThings()
{
    Task taskOne = PrintSomething( 2.0f, "One" );
    Task taskTwo = PrintSomething( 3.0f, "Two" );
    await Task.WhenAll( taskOne, taskTwo );
}

Calling Async from Synchronous Code

CSHARP
protected override void OnEnabled()
{
    _ = DoMultipleThings(); // fire and forget
    GetKanyeQuote().ContinueWith( task => Log.Info( task.Result ) ); // with callback
}

Lifecycle Safety

When a GameObject is destroyed or disabled while awaiting, the async method may still run. When awaiting via Component.Task, tasks are automatically cancelled if the GameObject becomes invalid. For custom implementations, use CancellationToken and check validity after awaits.

Avoid letting tasks stack up — handle cases where the user triggers an action multiple times during a wait.

Was this helpful?