Custom wait - the best solution to yielding!

Sorry I probably can’t (not on pc atm) but it happened to me in multiple places with different scripts.
Basically it yielded infinitely anything after the line with customwait does not worked.


local cwait = require(game:GetService("ServerScriptService").CustomWait) 

function module:New(n) 

And yeah I required both module scripts :smiley:.

Edit: Hmm thats odd maybe can this be caused that I actually clone the ModuleScript?

That example script works for me - I’m afraid this would be an issue on your end.

local Test = require(script.Parent)


Where can I take this module? or is it just the source code

Its just the source code. The source itself is pretty small, so I wouldn’t really see a need for a model, but I can’t speak for Pyseph.

1 Like

Oh never mind thanks, would this be good to replace all my normal wait() 's?
Want my game to be more functional

If your game uses wait() a lot, then yes. wait() being used more often can cause major slowdown for events in your game. If you want to know more, read this thread.

1 Like

I thought Heartbeat was better for this kind of stuff? :thinking:
Someone mentioned it to me and it made sense, so I don’t know if it’s something people wrongly think? I don’t know.

I’m pretty sure both are linked to physics simulation? (Or it’s just Stepped cause Heartbeat still works when you’re in Edit mode)

How does this compare to using tick(), like:

oldTick = tick()

if (tick() - oldTick) > 0.01 then

OldTick = tick()


(Just some pseudo code, but you get what I mean, using a loop for example)

Stepped and Hearbeat are both fired during a frame - Stepped is fired pre-simulation, Heartbeat is fired after.

I’m sorry but I’ve no idea what you’re asking here.

1 Like

I see now that my post was awful. So what I meant:
instead of writing;
while true do

you can write:

if tick()-oldTick > 1 then 
oldTick = tick()

so you are basically checking if the time difference is more than 1, instead of waiting 1 second before re-running the “loop”.

AND THAT leads me to another question:
is the tick() method just as good as your module, for all things loops? And what is the difference between @Maximum_ADHD 's module and yours? Thanks.

I want to ask this question just to clarify, I scrolled through all of the posts and haven’t seen this addressed.

Do you recommend that this module be used in any game regardless of wait count or frequency? Is it a guarantee that your performance will be better? Or do the performance benefits of this module only apply in situations where there is a significant number of yields to actually clog the task scheduler.

The latter - there’s not really a reason to use this unless you rely on yields quite a bit. My game doesn’t really use wait itself all that much, but it does use a custom version of Debris:AddItem, which uses this module.
It doesn’t do any harm to use this module even if your game isn’t calling yields a lot, though.

It is not necessarily true. I ran some tests with your module and the regular “loop with Heartbeat:Wait()”. The result showed “loop with Heartbeat:Wait()” is slightly more accurate. My test code and outputs are provided below.

Test code:

local o_clock = os.clock
local c_yield = coroutine.yield
local c_running = coroutine.running
local c_resume = coroutine.resume

local Yields = {}
	local Clock = o_clock()
	for Idx, data in next, Yields do
		local Spent = Clock - data[1]
		if Spent >= data[2] then
			Yields[Idx] = nil
			c_resume(data[3], Spent, Clock)

local function wait2(Time)
	Time = (type(Time) ~= "number" or Time < 0) and 0 or Time
	table.insert(Yields, { o_clock(), Time, c_running() })
	return c_yield()

local function loopHeartbeat(seconds)
	local elapsed = 0
	local timeout = seconds or 0

		elapsed = elapsed + RunService.Heartbeat:Wait()
	until elapsed > timeout

	return elapsed

wait(10) -- Wait 10 seconds for "Play Solo" to be stable

local function test(waitFunction, runCounts)
	local totalTime = 0

	runCounts = runCounts or 30

	for i = 1, runCounts do
		local waited2 = waitFunction(1)
		totalTime += waited2

	local averageWaitTime = totalTime / runCounts
	print("Average wait time: ", averageWaitTime)

	return averageWaitTime

print("Pyseph's BetterWait")
local result1 = test(wait2)

print("Loop with Heartbeat:Wait()")
local result2 = test(loopHeartbeat)

local delta = result2 - result1

print("Final Results: ")
print("Pyseph's BetterWait average wait time " .. result1)
print("Loop with Heartbeat:Wait() average wait time " .. result2)
	"More Accurate Function: "
		.. (delta < 0 and "Loop with Heartbeat:Wait()" or "Pyseph's BetterWait")
		.. " by "
		.. delta


  17:36:16.164  Pyseph's BetterWait - Playground:143
  17:36:17.180  1.0151325040024 - Playground:134
  17:36:18.196  1.0163139370015 - Playground:134
  17:36:19.197  1.0007079129973 - Playground:134
  17:36:20.214  1.0163980680009 - Playground:134
  17:36:21.229  1.0155252889999 - Playground:134
  17:36:22.230  1.0004381969993 - Playground:134
  17:36:23.246  1.0161530400001 - Playground:134
  17:36:24.264  1.0170518500017 - Playground:134
  17:36:25.280  1.0155368090018 - Playground:134
  17:36:26.281  1.000763101998 - Playground:134
  17:36:27.297  1.0158456979989 - Playground:134
  17:36:28.297  1.0000290450007 - Playground:134
  17:36:29.314  1.0163870389988 - Playground:134
  17:36:30.330  1.0162918210008 - Playground:134
  17:36:31.347  1.0161106450032 - Playground:134
  17:36:32.364  1.016458170001 - Playground:134
  17:36:33.380  1.016455691999 - Playground:134
  17:36:34.397  1.0159157490016 - Playground:134
  17:36:35.413  1.0164659180009 - Playground:134
  17:36:36.430  1.0164859329998 - Playground:134
  17:36:37.446  1.0157243880021 - Playground:134
  17:36:38.463  1.0169294400002 - Playground:134
  17:36:39.480  1.0161411480003 - Playground:134
  17:36:40.480  1.0000558619977 - Playground:134
  17:36:41.497  1.0163700119992 - Playground:134
  17:36:42.514  1.016410242999 - Playground:134
  17:36:43.530  1.0162811160008 - Playground:134
  17:36:44.547  1.0162930980005 - Playground:134
  17:36:45.563  1.016094224 - Playground:134
  17:36:46.580  1.0163273530015 - Playground:134
  17:36:46.580  Average wait time:  1.013569776767 - Playground:138
  17:36:46.580  Loop with Heartbeat:Wait() - Playground:146
  17:36:47.564  1.0000607641414 - Playground:134
  17:36:48.564  1.0002627000213 - Playground:134
  17:36:49.564  1.0000321837142 - Playground:134
  17:36:50.564  1.0001076254994 - Playground:134
  17:36:51.581  1.0168677251786 - Playground:134
  17:36:52.597  1.016533896327 - Playground:134
  17:36:53.613  1.0155636835843 - Playground:134
  17:36:54.614  1.0010616341606 - Playground:134
  17:36:55.631  1.0166026903316 - Playground:134
  17:36:56.646  1.0149364760146 - Playground:134
  17:36:57.647  1.0009081605822 - Playground:134
  17:36:58.663  1.0167570821941 - Playground:134
  17:36:59.664  1.0002399636433 - Playground:134
  17:37:00.664  1.0002204701304 - Playground:134
  17:37:01.680  1.0162733001634 - Playground:134
  17:37:02.696  1.0162025485188 - Playground:134
  17:37:03.713  1.0166019331664 - Playground:134
  17:37:04.730  1.0164914987981 - Playground:134
  17:37:05.732  1.0022880947217 - Playground:134
  17:37:06.746  1.014188173227 - Playground:134
  17:37:07.746  1.0003567114472 - Playground:134
  17:37:08.747  1.0000508250669 - Playground:134
  17:37:09.763  1.0163883510977 - Playground:134
  17:37:10.763  1.0000830888748 - Playground:134
  17:37:11.763  1.000117902644 - Playground:134
  17:37:12.763  1.0001743398607 - Playground:134
  17:37:13.779  1.0156670575961 - Playground:134
  17:37:14.780  1.0006323931739 - Playground:134
  17:37:15.797  1.0167331211269 - Playground:134
  17:37:16.813  1.0167141389102 - Playground:134
  17:37:16.813  Average wait time:  1.0083039511306 - Playground:138
  17:37:16.814  Final Results:  - Playground:151
  17:37:16.814  Pyseph's BetterWait average wait time 1.013569776767 - Playground:152
  17:37:16.814  Loop with Heartbeat:Wait() average wait time 1.0083039511306 - Playground:153
  17:37:16.814  More Accurate Function: Loop with Heartbeat:Wait() by -0.0052658256364035 - Playground:154

Edit: Swap “Heartbeat” with “Stepped” is more accurate.

local function loopStepped(seconds)
	local elapsed = 0
	local timeout = seconds or 0

		local _, delta = RunService.Stepped:Wait()
		elapsed = elapsed + delta
	until elapsed > timeout

	return elapsed

Your benchmark is flawed…
First off, you do not type check properly for whether the input is a number or not, and also whether it is smaller than 0 or not. This already makes your function more performant (and not to mention that you use Heartbeat instead of Stepped to make the comparison more equal).

Switching to Stepped and adding the number check, you get this (forgot to rename Heartbeat:Wait() to Stepped:Wait()):

Now, yes, this does seem as if the latter is better, but I’m doubtful you are going to have one yield in your whole game. Utilizing tables is inevitably going to be a better option for updating several waits - I in fact don’t even recommend using this module if you’re going to barely use yields! You should only use this when you’re starting to worry about clogging your task scheduler, which is the real enemy here.

EDIT: I realized I came off as rude here… Definitely not the intention. TL;DR is that you shouldn’t use this module unless you use wait a lot.

1 Like

One question I have about this is that what are the differences between this and CloneTrooper1019’s Thread function?

Only differences are:
A) this version is more compact
B) slightly more performant (you shouldnt need to care about this though), and
C) publicly available on devforum.

1 Like

Ok, thanks I’ll think about using this in my future projects.

Hey, I think you might have already talked about this, but I need a bit of clarification.
Is it performant to use wait() with an integer? Ex: wait(1)
Although I rarely use wait() by itself, I do use wait(integer) and the delay function quite frequently.
I’m writing the “engine” for a game I’m making, and I’d like to know what’s most performant early on before I have to sift through piles of code.

Depends. If you call wait(1) 100 times across your game code, I.e have 100 active yields, then it’ll start to turn a bit sour. The task scheduler gets flooded whenever it has to yield and resume a lot of threads - which is especially easy to occur when you call wait(). wait(1) can still flood the task scheduler, but it’s more unlikely. So in the end, it depends.