"Defer a synchronous blocking call a few frames so the loading overlay actually paints"
▸ SYMPTOM
A button click is supposed to show a loading overlay and then run a heavy job (a ~2-second synchronous, main-thread operation). Instead the UI freezes with no overlay, and the overlay only flashes in after the freeze — i.e. effectively never, since the work is already done by the time it paints.
▸ CAUSE
A razor onclick that flips an overlay flag and then immediately runs a synchronous job blocks the main thread before the next render pass. The overlay flag is set, but the render that would draw the overlay never gets to run until the blocking call returns — by which point there's nothing left to wait for. Show-and-block in the same handler can't work, because painting and blocking compete for the same thread and the block always wins.
▸ FIX
Split it across frames: the click shows the overlay and arms a short frame countdown; a separate OnUpdate decrements the countdown and runs the blocking work only when it reaches zero, so at least one render pass has painted the overlay first.
int _blockCountdown = -1;
void OnGenerateClicked()
{
Overlay.Instance?.Show(); // null-safe: still runs headless / from menu
_blockCountdown = 2; // let the overlay paint for a couple frames
}
protected override void OnUpdate()
{
if ( _blockCountdown < 0 ) return;
if ( _blockCountdown-- > 0 ) return;
RunHeavySynchronousJob(); // now the overlay is already on screen
Overlay.Instance?.Hide();
}Two details that matter:
- Keep the overlay call null-safe (
Overlay.Instance?.Show()) so the blocking work still runs when there is no overlay instance — headless, or from a menu context. This is the same defensive shape as a null-safe screen-fade. - The countdown field is not read by markup, so it stays out of
BuildHash— the overlay owns its own visible state and the countdown never triggers a spurious re-render.
▸ WHY IT WORKS
The overlay needs a render pass to appear, and a render pass only happens between frames. Deferring the blocking call by a frame or two guarantees the overlay has been painted before the main thread is monopolized, so the user sees the loading state during the freeze instead of after it. Two frames is enough headroom for the show to land; the exact count isn't critical as long as it's at least one.
- Published