[VIDEO] How to not use WHILE WAIT() loops

wait() in extreme situations can take up to 1 extra second when waiting. It’s unreliable. There’s nothing wrong with needing to do something with consistent timing.

Even more, it belongs to the secondary queue and all threads in the secondary queue are resumed after the primary queue. Not only that, it’s throttling is also FPS based.

The difference between tick() and os.clock() is that os.clock() is UTC, meaning it will be the same across all servers and clients, but tick() depends on the current timezone of the server/client that it’s used on. You don’t need to switch to os.clock() for things like game logic, but os.clock() is important for things like daily rewards.

Also know that tick will soon be deprecated, it’s obviously better to stay with what will be supported.

1 Like

Creating callbacks alone is not expensive, this can be backed by benchmarks. It can lead to performance impacts, such as memory leaks, over-connecting, embedded expensive code, etc.

Generally, using events instead of loops is considered more efficient on memory, but not always.

It’s best not to avoid threads as that can lead you down the wrong road in terms of optimization. It’s not the threads themselves, but rather, what you do with them.

In addition, connections are not threads, they are psuedo-threads, so it does work a little bit differently behind the scenes.

This sentence peaked my interest :thinking:
Where did you find this information?

1 Like

Creating a callback isn’t expensive, calling it is.

As I said, thread creation is expensive. Whatever you do in that thread determines the expensiveness of the thread, yes but do know that thread creation in general is expensive which is my point here.

Also, the information on ‘tick’ is available on the developer hub.

I did some searching and looked at the section about time on the June 2020 luau recap, and found important info.

@iGottic @SilentsReplacement The info about tick() being deprecated doesn’t seem to be on the devhub (i couldn’t find anything), but this post says they’ll be deprecating tick() in the future.

Seems like instead of os.clock() and tick(), time() should be used for game logic and os.time() should be used for daily reward systems (and things similar to that).


I believe my module would be perfectly suited in this situation: Custom wait - the best solution to yielding!
It’s basically a custom task scheduler, inspired by CloneTrooper’s tweet about how easily cloggable the task scheduler is.

1 Like

For your module should I always use this? Or only for while true do?

if debounce == false then
            debounce = true
            local Data = DataService:GetNumber(plr)
            Data.Germs += 1
            debounce = false

For example something like this.

Have you benchmarked any of this? I have boatbomber’s Benchmarker plugin so I can benchmark it later if needed.
Even then, I have a feeling that the performance cost would be so small that there’d be no reason to worry. Micro optimizations often make code look worse, and usually aren’t needed since the amount of processing they save is small.

1 Like

what about:

local RunService = game:GetService("RunService")

local Heartbeat = RunService.Heartbeat

local t = 0
local waitTime = 10

while t <= waitTime do
    t += Heartbeat:Wait()

-- or

local start = os.clock()
local duration = 10

while (os.clock() - start) < duration do

I’m just curious about it.

It should also be noted he isn’t creating a new thread. He is adding code to be ran each time the cpu cycles.

That’s how I do it but I still just do wait(). I only do the os.clock() method when im running stuff where millisecond differences matter (such as a speedrunning system)

1 Like

How not to use while wait loops: using them at all. Answered in four words. :laughing:

It’s not just while loops but loops and waits in general. Roblox developers have a bad tendency to use both of these things in cases where an event-driven system would better suit the current use case. There’s surprisingly not that many cases where you need loops, especially non-terminating ones.

I understand the need for loops and all that, but in most cases you don’t need one. RunService’s events aren’t considered loops, that pulls you into an event-driven system but you want to make sure that you’re actually using them where it counts, otherwise it’s no better than abusing the condition of while or taking advantage of the fact that wait returns truthy values.


Why would there be a need to benchmark this? It’s obvious that thread creation is expensive, quite significantly amount of work has to be done to create a new thread.

Only create new threads when they are really necessary, creating a new thread just to yield is unnecessary and unnecessarily expensive as well.

The code by @OP is not optimal, OP is creating a new thread which is unnecessarily expensive and will not even work properly to begin with.

Also, please read my post clearly, I never said that tick is deprecated, it will be soon. This information is available on the os library.

No, Connect calls the callback argument passed to it in a new thread. Since it’s connected via Heartbeat, it will call the callback every time heartbeat fires.

You always need to back up your points with evidence, whether or not you think it’s true.

I never said that tick() was deprecated?

1 Like

Here’s a benchmark of creating a thread. It’s not expensive at all.
(remember that the 50th percentile is more important than the max and average! outliers can cause those numbers to be very high, the 50 percentile gives the most accurate representation of how fast/slow the benchmark is)



If your tests alter a DataStore, it will actually alter your DataStore.

This is useful in allowing your tests to move Parts around in the workspace or something,
but with great power comes great responsibility. Don't mess up your stuff!


Documentation and Change Log:


return {

	ParameterGenerator = function()
		-- This function is called before running your function (outside the timer)
		-- and the return(s) are passed into your function arguments (after the Profiler). This sample
		-- will pass the function a random number, but you can make it pass
		-- arrays, Vector3s, or anything else you want to test your function on.

	Functions = {
		["coroutine.wrap()"] = function(Profiler, RandomNumber) -- You can change "Sample A" to a descriptive name for your function
			-- The first argument passed is always our Profiler tool, so you can put
			-- Profiler.Begin("UNIQUE_LABEL_NAME") ... Profiler.End() around portions of your code
			-- to break your function into labels that are viewable under the results
			-- histogram graph to see what parts of your function take the most time.
			-- Your code here
			coroutine.wrap(function() end)()

		-- You can add as many functions as you like!


This is possibly one of the most ignorant things to say when getting yourself involved in a debate, to be perfectly honest. Why would you put a claim out there if you can’t back it up?

This is especially stupid, considering that:

It’s not actually expensive.

Even further, I’ve used my methods countless times. I’ve created Yucon Framework, which runs games like Caliber Gun Demo. Both instances are optimized much better than an average game, so I can confidently say that threads don’t cause issues.


Threads don’t cause issues, but that wasn’t my point. Thread creation is expensive and thread creation in Lua is done internally in C, but since thread creation in C is fast, there won’t be any notice of expensiveness really but it is still something to take caution about.

For example:

Thread creation in other languages like Java is pretty expensive.

The benchmark shows it’s not expensive. Why are you continuing to say it’s expensive and that it should be worried about when it’s already been proven that it’s not expensive?

I don’t see how this is related to the conversation. We’re talking about Lua, not Java.


The benchmark shows it’s not expensive. Why are you continuing to say it’s expensive it’s not expensive?

No, thread creation in C is not expensive, but in other languages, it is. Creating a new thread in Lua, that is done internally in C.

and that you should be worried creating threads

Had gone a little off-topic there, my bad.

I don’t see how this is related to the conversation. We’re talking about Lua, not Java.

That is just an example, thread creation in C is not expensive, but that doesn’t mean that thread creation in other languages isn’t expensive which you’re tried to prove but failed. “thread creation is not expensive”, should have said it as “thread creation in Lua © is not expensive but in other languages it is”.