Avoid using while true do & while wait() do!

I know it is faster, but a point of it being faster is that it will wait for a more accurate time, but in the example given the old wait() function was closer to 2 than the new task.wait() function?

1 Like

[This is a nit-pick reply]
I think the title is a bit misleading/off-putting. There’s nothing wrong with while true do in this case:

while true do
     task.wait(3)

     --// Rest of code
end

While this is functionally the same as while task.wait(3) do, it’s syntactically clearer and more precise to do while true do and then have the rest of the function follow. Again, I realize this is a nit-pick, but I still think it’s worth noting, because it puts it at odds with the topic title in it’s current form “Avoid using while true do & while wait() do!”. This topic might be better renamed as “Best practices for while loops?”

17 Likes

wait() will return the time elapsed while task.wait() does not. Though I’m still not sure why people use Spawn() and Wait().

1 Like

Instead of using while loops with task.wait, which yields, I prefer to connect to heartbeat or any of the other render functions instead so it doesn’t yield. Not quite as pretty and a bit less intuitive, but still gets the job done and to my knowledge doesn’t yield the thread. It should also work with unlocked frame rate as it uses deltatime instead of an arbitrary wait time

local Interval = 1
local lastActivation = 0

RunService.Heartbeat:Connect(function(dt)
      if lastActivation >= Interval then
            DoWork()
            lastActivation = 0
      else
            lastActivation += dt
      end
end)
3 Likes

Why from 0.8? This doesn’t time out for me.

while task.wait() do

end
1 Like

I’ve never, NEVER, had scripts time out for me or studio crash despite having atleast a dozen using a setup like this

while true do
   --insert action here
end

or like above but with a wait time of under 1.

If that somehow crashes your studio or times out your scripts, quite frankly, your studio or system may just be unstable/unfit. Or you had an unrealistically excessive amount of scripts in a single place, my assumption, because I don’t know.

1 Like

Although you have the right idea in some cases, I don’t think you sufficiently understand the practices you’re trying to teach and that leads you to providing wrong teaching material on this topic. This thread is effectively a long form post of reminding developers not to leave a loop body without a yield at the end of an iteration or it may hang the environment it’s running on.

The main problem with while loops are either lack of intervals by nature of a yielding condition (be it task.wait or a yielding function call) or yielding at the start of an iteration which makes the loop somewhat unmaintainable. Ideally you only want to wait as in when you need to.

Using wait in the conditional part of a while loop is bad practice whether or not you provide a value. Wait will provide a return value which will render the conditional of a while loop truthy but you’re changing that evaluation every iteration. In general, you should strive to avoid a yield as the conditional and instead make sure that the evaluation is static. If you need to return something, it should be ensured that the function stack yields as little as possible or doesn’t at all.

That being said, while loops themselves are not bad practice, so the thread is misleading by saying to “avoid both” but then suggesting those exact same things with changes to them, particularly the former by having a yield condition at the end of the loop body and the latter by giving an argument to wait. It is generally already expected that a non-yielding while will crash.

Not for reason of crashing but of practice, I would recommend taking a look at this resource which directly explores the real problem with while loops which is abusing the conditional:

7 Likes

that moment when i pcall in a while true do end and i forget about the task.wait() after the pcall (if unsuccessfull)

jokes aside this is a good post, might use it

1 Like

This is actually better in my opinnion, but in alot of my projects, I suffer from lag in while loops that check for each and every bool value and it just shoots remote events repeatedly for the player’s gun. I made 3 more weapons since a pistol wasnt enough and 3 because I use GUI for my weapons since there is more guns, it also would have another 3 while loops running at the same time. I think i might make a post on how you can help me with this before I even give it away to my brother.

1 Like

They both return elapsed time.

2 Likes

Only wait() does:


22 seconds elapsed in the game.

2 Likes

I know most people do

while true do
 -- Code
 task.wait(1)
end

But what about

while script.Parent do
 -- Code
 task.wait(1)
end

or

repeat
 -- Code
 task.wait(1)
until not script.Parent

?

I see almost no one talk about that.
Theoretically if you do script.Parent the code should execute until the script is destroyed or parented to nil.

OH here is another fun loop.

function doThing()
 -- Code
 task.wait(1)
 doThing()
end

doThing()

Although that one might stop working if there is a limit on how often recursion can be done.

Edit: Also yes, I already know destroying a script stops it’s execution.

2 Likes

Destroying a script kills the thread the script depends on, ie: preventing any wait statements from resuming since its dead

1 Like

I completely agree. This post is structured very odd because it makes it seem like you should use while true do, but that’s not the point of the post at all. The point of the post seems to be reminding people to add task.wait() when using this loop.

When I finished reading this post I was so confused…Telling us to stop using it, but then concluding the post by telling us to continue using it.

1 Like

TL;DR
The problem is not in the loop itself, it’s in the absence of a “wait”, meaning it will run at an incredibly fast speed, causing the game to lag or even crash.

This actually did leave me wondering if destroying a script also destroys connections made to events.

I often feel like that if a script is destroyed that events just keep existing and stacking up in memory but with no code to execute because the script that holds the function is gone.

The difference you are seeing is approximately 1/71th of a second off (less than one frame). This is because task.wait resumes on the first frame after the time has elapsed. It will be at most one full frame off (it is accurate to within one frame). The reason that it appears less precise in your example is purely by chance.

wait can also do this too, but it will be at most two full frames off, and often times will be worse, particularly when the server is under high stress, because wait works differently under the hood.

Depending on how long different frames happen to take, task.wait and wait can both be off by that amount, it just depends how long it will be until the next frame, and frames can take any amount of time and almost definitely will across different tests. task.wait is still much more accurate due to it always resuming on the first frame.

2 Likes

Apparently, wait() doesn’t have much of a difference from task.wait() so it’s gonna throw an timeout error at you. You have to use task.wait(0), and because the accuracy is spot-on for this function, it’ll work gracefully.

I have a slow computer. Real slow, but it worked.

Not sure when using loops was a bad thing? Just because they can crash if not used properly is not a good reason.

1 Like

I don’t really see the point of using

while true do
	task.wait(1)
end

since all it does is run in an infinite loop that executes about every 1 second. However, a better use for that is this:

task.spawn(function()
	local start
	while true do
		start = os.clock()
		for _, item in pairs(some table) do
			-- Do some work.
		end
		task.wait(1 - (os.clock() - start))
	end
end)

That’s one pattern that I’ve used it for. Why would I use something like this? To perform background processing at timed intervals. I’ve used this on both client and server code. Works quite well. For the OP to state that people should avoid using while true do just because one can easily screw things up with it is utter nonsense and demonstrates that the OP does not understand software development constructs or why certain patterns are used.

The reason why

while true do
	print("Hello World")
end

appears to crash Roblox is that there is no opportunity for the task scheduler to gain control which is why Roblox will freeze. Unless you’re using Actors for Parallel LUA, Roblox LUA script execution is serial between threads. If the current thread does not yield (which is what wait() and task.wait() is for), then the other threads that are waiting to run can’t and the system locks up. Roblox LUA has a script timer so if the run time exceeds that, the engine throws an exception and breaks the LUA execution. So in other words wait() and task.wait() are used in what is known as a cooperative multithreading system to yield the current thread.

Here’s another:

local loopflag = true
while loopflag == true do
	loopflag = false
	for _, item in pairs(some table) do
		if <some condition> then
			-- Do some work.
			loopflag = true
		elseif <some other condition> then
			-- Do some other work.
			loopflag = true
		else
			-- Exit the for loop
			break
		end
	end
end

The above construct is what I use if the predicate for the while condition is too complex to express in one line, or there are a complex set of conditions that must be met for the loop to exit or continue.

In software development, there are three basic constructs of code:

  • Sequence: Instructions/Statements are executed one right after the other.
  • Condition: The instruction sequence that is executed is based on some condition; branching.
  • Loop: A set of instructions are to be executed none, one, or more than one time.

These constructs are needed for a language to be Turing complete.

As for the difference between wait() and task.wait(), the difference is simple:

  • wait() waits for about 1/30th (0.033) second before resuming the thread execution. This is one frame.
  • task.wait() waits for about 1/60th (0.016) second before resuming the thread execution. This is two frames. However, in reality, it’s closer to 0.0202 to be honest.

Nothing wrong with using wait() if you only need a 1/30 second delay as opposed to a 1/60 delay.

Of course, it all comes down to this adage that I’m sure everyone has heard before:

The right tool for the job.