First Across the Line: rush keeps the losers running
In this lesson you'll learn to
- ✓Student can choose between race and rush — rush returns the first result but lets remaining tasks continue.
Student can choose between race and rush — rush returns the first result but lets remaining tasks continue.
📖 Reference & full walkthrough: First Across the Line: rush keeps the losers running
🧩 Your capstone piece: Winner detection: rush over per-racer finish awaits crowns first place while others keep racing for podium spots
What you'll learn
In Verse, time flow is a first-class language feature. Normally code runs top-to-bottom, one expression after another. But games are inherently concurrent: enemies spawn, HUDs animate, and timers tick all at the same moment. The rush expression groups two or more async expressions (functions marked <suspends>, Sleep, event awaits) so they start together.
The key behavior that sets rush apart from race: when the fastest subexpression completes, the expression that follows rush is evaluated — but the slower subexpressions are not canceled. They keep running to completion. That makes rush ideal for kicking off independent background work while reacting immediately to the first result.
How it works
Think of rush like a director shouting "Action!" at several actors simultaneously. All subexpressions begin at once. As soon as one finishes, your code moves on, but the others continue in the background.
A few rules that keep rush correct:
rushmay only appear in a<suspends>context (likeOnBegin), because its subexpressions are async.- Every subexpression must itself be async — calling
Sleep, awaiting an event, or invoking a<suspends>helper. - To actually accomplish something visible, our subtasks call real player-UI APIs. We reach the HUD with
Player.GetPlayerUI[](a<decides>call, so it goes in anif) and push widgets withPlayerUI.AddWidget(Widget).
We build a text_block widget, show it immediately in one subtask, then in a second subtask wait two seconds and swap the displayed text. Because both subtasks live inside rush, the immediate update and the delayed update are scheduled concurrently and neither blocks the other.
Let's build it
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Fortnite.com/Devices }
using { /Verse.org }
using { /Fortnite.com/UI }
# A device that shows a HUD message, then concurrently updates it after a delay using `rush`.
rush_ui_device := class<concrete>(creative_device):
# Async helper: show initial text on the player's HUD immediately.
# It is <suspends> so it can be a rush subexpression; Sleep(0.0) yields once.
ShowOnline(TextWidget:text_block)<suspends>:void =
Print("Task 1: pushing 'System Online' to HUD")
TextWidget.SetText(StringToMessage("System Online"))
Sleep(0.0) # yield to other concurrent tasks this frame
Print("Task 1: HUD update sent")
# Async helper: wait, then swap the HUD text. Runs concurrently with ShowOnline.
ShowComplete(TextWidget:text_block)<suspends>:void =
Print("Task 2: waiting 2 seconds...")
Sleep(2.0)
TextWidget.SetText(StringToMessage("Task Complete"))
Print("Task 2: delayed HUD update sent")
# Convert a plain string into a message for the text_block.
StringToMessage<localizes>(Value:string):message = "{Value}"
OnBegin<override>()<suspends>:void =
# Reach the first player; GetPlayers() returns an array, array{0} is fallible.
if (Player := GetPlayspace().GetPlayers()[0]):
# GetPlayerUI is <decides> — call it inside the if and bind the result.
if (PlayerUI := GetPlayerUI[Player]):
# Build a real text widget and add it to the HUD before rushing.
Label := text_block{DefaultText := StringToMessage("Booting...")}
PlayerUI.AddWidget(Label)
# rush: both subtasks start together. When the fastest (ShowOnline)
# completes, execution continues past rush; ShowComplete keeps running.
rush:
ShowOnline(Label)
ShowComplete(Label)
Print("rush returned (fastest task done); slower task still finishing.")
Try it yourself
- Add a third subtask to the
rushblock (for example, one thatSleep(1.0)then prints) and observe that the line afterrushfires as soon as the fastest one finishes, while the others continue. - Swap
rushforraceand watch the difference:racecancels the slower tasks, so the 2-second update never appears. - Replace
Sleep(0.0)inShowOnlinewithSleep(3.0)so it becomes the slowest. Notice the post-rushline now fires whenShowCompletefinishes instead. - Capture a
rushresult by assigning it:Result := rush: ...and feed the winner into your next expression.
Recap
rushruns two or more async expressions concurrently and continues as soon as the fastest completes — without canceling the slower ones (unlikerace).- It must live in a
<suspends>context, and every subexpression must be async. - We performed the technique for real:
GetPlayerUI[Player](a<decides>call insideif) plusAddWidgetandtext_block.SetTextto drive a live HUD from concurrent tasks. - Use
rushfor independent background work where one immediate result shouldn't wait on slower side effects.
Keep going — free
You've read the intro. The rest of this lesson is free for members. Sign in to continue and track your progress.
Sign in free to continue this lessonSources
/guides/sug-implementing-concurrent-logic-with-rush-expressions-in-verseBiloxi Studios original lesson
Add this lesson to your free study plan.
🧭 The Keeper’s log
Quest complete? Chart your next heading from the 🦑 West Coves expedition.
⛵ Take me there