Await (for all your event races, waiting for specific fired arguments, with timeouts)

Functions:

-- Only stops yielding if specific args are passed (or timeout reached)
Await.Args(timeout, event, ...)

-- Only stops yielding when event fires (or timeout reached)
Await.Event(timeout, event)

-- Only stops yielding when one event of multiple fires, optionally with specific args passed (or timeout reached)
Await.First(timeout, ...)

General use case:

local events = {}
local timeoutDuration = 5 -- or nil

local timedOut, winnerKey, winnerArgs = Await.First(timeoutDuration, unpack(events))

if not timedOut then
   print(winnerKey, "was the first event fired with these args:", winnerArgs)
else
   print("no events were fired after", timeoutDuration, "seconds")
end

Use cases from my games:

Yes/no buttons
local _, winnerKey = Await.First(nil, unpack(self._yesNoButtonEvents))

if winnerKey == "yes" then
   ...
elseif winnerKey == "no" then
   ...
end
Retrieve player data timeout
local timedOut = Await.Args(10, profileLoaded.Event, player)
		
if timedOut then
	warn("Could not retrieve profile replica for " .. player.Name .. ". Reached 10 second timeout")
	return nil
end
Waiting for one event to fire before the other
local _, winnerIndex = Await.First(nil, player.Destroying, fishNameGui.Destroying)

if winnerIndex == 1 then
	fishNameGui:Destroy()
end
Advanced inventory use case
-- A little context. This is apart of inventory gui code, when the player right clicks on an item
-- in their inventory, a frame pops up with options like "equip, unequip, drop, sell". The event
-- race is to determine which they click first, or if they click off of the frame (causing it to disappear),
-- or if they close the inventory all together with 'G'

local eventRaceArgs = {
	{
		UserInputService.InputBegan, function(input) -- This is for if the player closes the inventory or clicks somewhere else on the screen (unfocusing the current opened options frame)
			return input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.MouseButton2 or input.KeyCode == Enum.KeyCode.G
		end
	}
}
	
for _, button in ipairs(itemInteractOptions) do
	button.Visible = mode:match(" " .. button.Name:lower())

	if button.Visible then -- If the button is visible, then it's a viable option. We'll insert this into the event race
		table.insert(eventRaceArgs, {
			button.TextButton.MouseButton1Down,
			Await.WinnerKey(button.Name)
		})
	end
end
	
task.spawn(function()
	local _, buttonPressed = Await.First(nil, unpack(eventRaceArgs)) -- Waiting for the first event to fire
		
	disableInteractOptionsFrame()

	if type(buttonPressed) == "string" then
		interactOptionCallback(buttonPressed)
	end
end)

Roblox Model: Await - Roblox

2 Likes