While wait() do vs runservice renderstepped

So this is my code

Can someone explain why using while wait() do was laggier than runservice renderstepped?

I want to make a minigame so should i use while wait() do or runservice renderstepped for the loop?

the wait(100) is temporary

wait() will yield the code for a minimum of two frames. On the other hand, task.wait(), RenderStepped:Wait(), Heartbeat:Wait(), and Stepped:Wait() will all yield for just one frame.

Usually, it’s discouraged to use while [true] do loops for repetitive tasks, since it can become cumbersome to break out of the loop (the break statement needs to be inside the loop itself which can be difficult to manage).
Instead of while task.wait() or while RenderStepped:Wait(), you should be using the RBXScriptConnections associated with them instead.

2 Likes

so what should i use for my minigame script?

Answer that for yourself, should the loop be faster? Does it need to update every single frame? Does it need to look “smooth” to the player? Will it have performance implications if I make it update twice as fast?

I don’t know anything about your game, but you do.

would you recommend me to use renderstepped or while wait() do

Tachiyen, pretty much answered your question already:
He said:

Instead of while task.wait() or while RenderStepped:Wait() , you should be using the RBXScriptConnections associated with them instead.

So you should use events instead of task wait() or renderstepped for what you are trying to do. Find an event like playeradded or characteradded to do what you need. And you should probably avoid things like this:
if (Holder:FindFirstChild..... then
Holder:FindFirstChild(same stuff)

You are doing the search twice on the same object

Instead define a local var and test the var like
local result = Holder:FindFirstChild...

if result ~= nil then result:Destroy()

roblox now has a new task library: Task Library - Now Available!

and it is said here that
image

so its a good idea to use task.wait()
and task.wait() is equivalent in behaviour to RunService.Heartbeat:Wait()

1 Like

What you are doing is known as Polling. Polling is when you are constantly checking for something even if it’s false. This means your wasting processing power

Instead of checking if an instance exists and then destroying it, just use the given RBXScriptSignals that not only are more performant but also garuantees it runs immediately

Here’s a rewrite with event-driven functions

local Holder = script.Parent:WaitForChild("Holder")
local Bar = script:WaitForChild("Bar")

function createBar(Player)
	local String = "@"..Player.Name
	
	local newBar = Bar:Clone()
	newBar.Name = String
	newBar.PlayerName.Text = String
	newBar.Visible = true
	newBar.Parent = Holder
	
	local Char = Player.Character or Player.CharacterAdded:Wait()
	local Root = Char:WaitForChild("HumanoidRootPart")
	
	-- Looping is the only way to get the Position since connecting events to changes in physics doesn't do anything
	local Loop = game:GetService("RunService").Heartbeat:Connect(function()
		newBar.Position = UDim2.new(0,0,0.9-(Root.Position.Y/100),0)
	end)

	Player.Destroying:Once(function()
		Loop:Disconnect() -- Make sure it gets Disconnected to prevent a memory leak
		newBar:Destroy()
	end)
	
	Player.CharacterRemoving:Wait()
	task.defer(game.Destroy,Char) -- Make sure the Character really does get Destroyed
end

--//

for _,v in game:GetService("Players"):GetPlayers() do
	createBar(v)
end

game:GetService("Players").PlayerAdded:Connect(createBar)

game:GetService("Players").PlayerRemoving:Once(function(Player)
	task.defer(game.Destroy,Player) -- Make sure the Player really does get Destroyed
end)

For future reference, never use while wait(n)/task.wait(n) do. That idiom abuses the fact that wait/task.wait returns a truth-y value and really isn’t performant. If you do need code to run every once and a while just count the DeltaTime for Heartbeat

local elapseTime = 0

game:GetService("RunService").Heartbeat:Connect(function(dt)
	elapseTime += dt
	
	if elapseTime >= 10 then
		elapseTime = 0
		
		-- stuff
	end
end)
2 Likes

That lib is an improvement over the old equivalents, however, the new versions still have similar drawbacks compared to event handling. The task lib is convenient, but not the ideal or best performant solution. So, they are convenient, but hardly a good idea.

so are you saying that

game:GetService("RunService").Heartbeat:Connect(function(deltaTime)

end)

is more performant then

while true do
	local deltaTime = task.wait()
end

because i see no difference in the micro profiler code inside Heartbeat runs at the same speed as code put inside the while loop

2 Likes

I don’t think the while loop is correctly utilized. Also I’m not exactly sure about while task.wait() do either. You should only use the while loop when a statement is needed to stop it, unless it is intentionally an infinite loop for a specific design of a flow.

No, I’m not saying that at all. I’m saying running a while loop that loops over all the players and searches strings, researches the same string and creates instances and parents parts, possibly unnecessarily, is not as performant as using an event to do the task only when needed. In this case, the performance we are getting from events is spreading the work out over time so there won’t be a giant loop delay and prevents execution when there is no work to be done. I was comparing the while loop processing to event processing.
As to your example, the heartbeat function runs at the frame rate of your game. If you need something to run each frame for certain performance characteristics, this would be a good choice. But, it won’t be a good choice if the task you are doing involves ALL the players, i.e. is not brief or cannot be completed faster than the frame time.
The while loop using the task wait() won’t be a good choice if the platform you are running on is busy and can’t service the loop when the wait time expires especially when you choose a short wait time. This is probably the case that causes lag the most when the expected frequency of this loop fails. If you simulate this scenario in your test example, you will see the heartbeat is smoother.
In short, it depends on what type of performance you are looking for that dictates what pattern you should use to solve your requirements. T
Tachiyen said pretty much the same thing above when he wrote:

Answer that for yourself, should the loop be faster? Does it need to update every single frame? Does it need to look “smooth” to the player? Will it have performance implications if I make it update twice as fast?
When you answer these questions you will know if you should use events, while wait, heartbeat, whatever.

oh I was mistaken when you said event handling I assumed you was talking about runservice events but yes i agree with you that only running code on events that fire sometimes is better then running code every frame but the ops question was Can someone explain why using while wait() do was laggier than runservice renderstepped?

and if you look at the screenshot the op is moving gui based on all the players Y position and i’m not aware of any events to detect when a players position has changed and the position of characters move so often that it will most likely need to be done every frame so events is not really a option for the op


here is a demo project of how you can set GUI based on the players height
PlayerHeightGui.rbxl (40.4 KB)

and this is the code used inside the demo project

local runService = game:GetService("RunService")
local playersService = game:GetService("Players")
local imageLabel = script.Parent.ImageLabel
local players = {}
local height = 100

local function PlayerAdded(player)
	local clone = imageLabel:Clone()
	clone.Image = playersService:GetUserThumbnailAsync(player.UserId, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size60x60)
	clone.Parent = script.Parent
	players[player] = clone
end

local function PlayerRemoving(player)
	players[player]:Destroy()
	players[player] = nil
end

local function Heartbeat(deltaTime)
	for player, imageLabel in players do
		if player.Character == nil then continue end
		local y = math.clamp(player.Character:GetPivot().Position.Y / height, 0, 1)
		imageLabel.Position = UDim2.new(0.5, 0, 1-y, 0)
	end
end

imageLabel.Parent = nil
for i, player in playersService:GetPlayers() do PlayerAdded(player) end
playersService.PlayerAdded:Connect(PlayerAdded)
playersService.PlayerRemoving:Connect(PlayerRemoving)
runService.Heartbeat:Connect(Heartbeat)

image

replacing runService.Heartbeat with while true do task.wait() end would give the same results in this demo project both methods will run every frame

1 Like