Best way to yeild for something?

What is the best way that I can yeild for something?

  1. Bindable Events
  2. Coroutine Yeild (although I’ve never used coroutine and I do not know how to use this)
  3. Repeat wait() until

I want to yeild my player functions until their player data is loaded, im currently using repeat wait() until but i know that this is frowned upon. How would I do this in a better way?

local Data = _G.PlayerData

game.playeradded:Connect(funciton(plr)
 repeat task.wait() until Data[Player]
end)

It’s not frowned upon if it’s the only way.

Are you sure its the only way? Some people use bindable events to yeild

Are you asking how to use bindable events to yield?

image

You could I guess make a ‘Loaded’ bindable event and wait for it to fire with the player you want. But, why don’t you just add your code after where the data loads? Then it will wait the exact time it needs.

Im asking for the best way to yeild until something happens, weather thats bindable events, couroutines, or repeat wait until

I have multiple scripts that use the player data

Using the task or coroutine libraries is the best way to “yield until something happens”, generally speaking.

how do you yeild with the task library?

Using the API often automatically yields within the calling function… so in your original, it yields the first time task.wait is called (and then continues yielding every time)

To your original goal, I don’t think “yielding” gets you what you want though.

You’ll need to structure your “player functions” to run after [some event] where you can use a Bindable.Event, remote function or remote event, depending on where your “player functions” are and what they need access to.

Like @bluebxrrybot said, it’s not frowned upon.

local function YieldUntilDataLoaded(player)
	while not LoadedData[player] do task.wait(.1) end
end


Players.PlayerAdded:Connect(function(player)
	YieldUntilDataLoaded(player)
end)

This is how I yield until the data is loaded for the player and works fine for me.

Tangentially related:

Event-based programming isn’t the be-all and end-all of performance. For the few milliseconds of performance you’re saving (if you’re saving any, at all), you’re costing time in readability. I don’t want to have to spend time finding and connecting some event responsible for one menial task - so I doubt others would too. There’s a reason high-level languages are often multi-paradigmed.

Practically all of my scripts involving a player’s character are written like:

local PLYRS : Players = game:GetService("Players")
local plyr : Player = PLYRS.LocalPlayer or PLYRS:FindFirstChild(foo)
local char : Model = plyr.Character
local primary : Part = char.PrimaryPart

while not char or not primary do
    char = plyr.Character
    local primary = char.PrimaryPart
end

@colbert2677 @SPOOK_EXE
Thoughts on this?

I actually do have a system working like this but theres an issue.
I have some scripts that wait for:

  1. Server Initiated Player
  2. Client Loaded Player
  3. Data Loaded Player

Coordinating multiple asynchronous events can be challenging, depending on design constraints and requirements but entirely doable with events. It’s hard to give application design-level advice when talking in the abstract though - I’d need to know quite a bit more about said “design constraints and requirements” to provide help… or even that async events are the right solution. As @Abide5178 said, async comes with it’s own overhead in complexity that should only be taken on when required.

1 Like

It’s less about performance but more about canonicity and how you can tie your work with what the engine can afford or what you can come up with. When a yield is required, I will always have an event set up, whether it relies on an engine event or a pure Luau signal. For asynchronous yields that I have no control over, I deploy Promises to call the function and give me the result to chain off of.

Regarding that first bit: the engine is very powerful so the time gains you have between a signal and a loop are very trivial, so it’s not always good to just talk about designing for performance. There’s other factors like making your code readable, maintainable, idiomatic, future-proofed, and more.

There is never a time I will agree where a loop is more suitable than an event. An event can replace a loop but a loop can’t replace an event, only work together with one. If there is an event to do what needs to be done, I will always recommend it more than the loop. On Roblox, engine events have the significant win over loops in readability and efficiency.

When you use an event or even some method that performs a task a certain way, your code is pretty future-proofed. A nice example is DistanceFromCharacter which calculates the distance between a given Vector3 point and your character. This turns over the logic of checking distance to the engine which may update how distance is calculated (e.g. different limb, center position or parameters for a valid check) and save you the trouble of needing to update so many different pieces of code to accommodate any new behaviour.

There’s a very good reason why we have the Wait method available on RBXScriptSignal and it’s so that you don’t have to form a connection for a “menial task” – it allows you to get whatever arguments the signal fired with right away. I don’t fancy the case of iterations repeatedly running when they don’t need to, and prefer to yield my code until events fire which then signal the scheduler to resume the thread.

My advice is that you should find those events you need, or build a system dedicated to the task that you want - don’t use loops unless you need them.

local Players = game:GetService("Players")
local LocalPlayer = Players.LocalPlayer -- Implicit to a LocalScript
-- If there's no character, return what CharacterAdded fires with next,
-- which is guaranteed to be a character model.
local Character = LocalPlayer.Character or LocalPlayer.CharacterAdded:Wait()
-- Internal equivalent is a FindFirstChild, and if nil, then ChildAdded:Wait()
-- until a match is met. Optional timeout to end this process early.
local RootPart = Character:WaitForChild("HumanoidRootPart")

This is much more readable than any loop I would ever hope to think of for a similar case on the post you were asking for input on, and maintainable. In Deferred mode you don’t even need to wait for the HumanoidRootPart because the event is arriving later in the frame. Personally, if anything, I have the exact opposite view of that post - I would hate writing a whole loop to accomplish something trivial, and would only do so if there’s no other option (obvious examples include traversing tables).

Ultimately though, Promises are surely the way to go when you want to handle coordination of multiple asynchronous events, chain off them and solve some really common issues with yields. An example of where Promises saved me was in fact the very same case as yours with yielding for data. I have a method that allows scripts to wait for data which in turn will see what happens first: the data loads (or is loaded) and gives back the data, the data unloads and errors with nothing to return or the player leaves so it errors with nothing to return.

No way I’d use a loop to handle complex logic like this. It’s all based in events and it’s not terribly unreadable either.

4 Likes

A good illustration of how programming logic is thought through, and how people might approach the same task differently.

Appreciate the depth and clarity your reply lends! :slight_smile:

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.