Custom wait - the best solution to yielding!

Update [1.0.6]

Addressed issues where calculating the most-prioritized yield did not take into account for current time. This means that this script:

spawn(function()
	while true do
		print("a")
		BetterWait(0.1)
	end
end)

BetterWait(1)
print("b")

Would never get to “b”, because BetterWait(0.1) always had more priority over BetterWait(1).

2 Likes

Roblox now has a time property.

1 Like

It always did, but it started becoming more prominent recently. I’ll switch to time soon

1 Like

Update [1.0.7]

  • Fixed a possible script exhaustion error, which occurred when you had over 100 wait loops running at the same time - I didn’t know about this because, well, I don’t have 100 wait loops running at once lol.

  • Optimized code overall - it now runs 5x faster than the previous version :stuck_out_tongue:

3 Likes

Ok my code:


local function Loop(self)
	while true do
		--doing something works!
	self.DATA:Wait(0.01) end
end

local function Display(self)
	while true do
		--runs once and breaks loop
	self.DATA:Wait(0.01) end
end

local function Clean(self)
	while true do
		--this one also runs good
	self.DATA:Wait(0.01) end
end

function obj.new(DATA)
	local self = setmetatable({}, obj)
	
	coroutine.wrap(Loop)(self)
	coroutine.wrap(Display)(self)
	coroutine.wrap(Clean)(self)
	
	return self
end

I know the Display loop works because if I replace it with normal wait(), it works good and doesnt break.

Hey!
I’ve actually noticed this issue yesterday and was up till 3 AM trying to fix it, but I only managed to address the issue today. Can you update your module’s source to the newest version, and see if your issue has been addressed?

EDIT: Seems like it wasn’t fixed according to my testing. Looking into it!

1 Like

Update [1.0.8]

  • Addressed issue where a yield wouldn’t work properly (@V3N0M_Z)
  • Optimization of code to function faster (I’ll show a benchmark to compare all solutions in a moment)

Glad you figured out a solution. I will check it out when I get back on. But otherwise, exceptional job making this module. Looking forward to more of your potential projects. :grin:

So I wrote my own custom yield script, just for fun, and after doing a benchmark it seems to be functioning better than this one.

Benchmark script:

-- // By StrategicPlayZ \\ --

local FastWait = require(workspace.FastWait)
local CustomWait = require(workspace.CustomWait)

for i = 5,1,-1 do
	print("[STARTING BENCHMARK IN " .. i .. "s]")
	wait(1)
end

local total = 50000
local wait_time = 1

local benchmark_func = FastWait do
	print("...[BENCHMARK BEGIN]...")
	
	local num_failed = 0
	local total_wait = 0
	local highest_wait = 0
	local lowest_wait = math.huge
	
	local completed
	
	game:GetService("RunService").Stepped:Wait()
	
	for index = 1, total do
		coroutine.wrap(function()
			local taken_time = benchmark_func(wait_time)
			if ((taken_time > (wait_time + 0.04)) or (taken_time < (wait_time - 0.04))) then
				num_failed += 1
			end
			if (taken_time > highest_wait) then
				highest_wait = taken_time
			end
			if (taken_time < lowest_wait) then
				lowest_wait = taken_time
			end
			total_wait += taken_time
			completed()
		end)()
	end
	
	do
		local n = 0
		function completed()
			n += 1
			if (n ~= total) then return end
			
			local average_wait = (total_wait / total)
			
			print("[% NUM FAILED]: " .. (num_failed / total * 100) .. "%")
			print("[AVERAGE WAIT]: " .. average_wait)
			print("[HIGHEST WAIT]: " .. highest_wait)
			print("[LOWEST  WAIT]: " .. lowest_wait)
		end
	end
end

Result (custom wait):
Screenshot_10

Result (my wait module):
Screenshot_11

https://www.roblox.com/library/6356332088/FastWait

After slightly altering my code, the end result was this:

  • Yours
    image
  • Mine
    image

All I altered was the for loop in the Stepped event, editing it from 10,000 to 100,000. By doing so, it effectively unyielded all the yields at once, rather than taking 5 Stepped events to unyield them, since it only unyielded a maximum of 10,000 per Stepped. I set this limit with script exhaustion time in mind, which your script does not take into account for. By raising the for loop limit, this module effectively addresses the flawed benchmark.
Either way, I will not be raising the unyield limit from 10,000 since as stated, it could cause havoc when you have 100+ while true do wait() end loops.

3 Likes

Screenshot_8

I set mine to 100k, it still shows similar result, I even set it to while true loop, it still shows some fails.

By the way forgot to mention I have edited the module with os.clock instead of time since it is much more accurate.

This is the result when I changed os.clock back to time().

Screenshot_9

You too try with os.clock instead.

Here is the result of my module:
Screenshot_10

And no it’s not os.clock being inefficient, as you can confirm it from the following code in the command bar:
(Run this when the game is running.)

local a = time()
print("a")
print(time()-a) -- OUTPUTS 0s
local a = os.clock()
print("a")
print(os.clock()-a) -- OUTPUTS SOMETHING DIFFERENT, IN MY CASE: 0.000154
1 Like

This seems like an issue of having different hardware in that case. I don’t really bother comparing stuff at such a level, since it’s very prone to illusionary and misleading results - the only reason I compared this module with Roblox’s is that the margin of error is small enough in comparison to Roblox’s “failure rate”, and as such the margin of error can be ignored completely. With modules that provide very similar results with only the slightest differences, it’s hard to say what’s truly the better option. I’ll run both scripts though the micro-profiler and also boatbomber’s benchmarker plugin instead, as they provide with more accurate results.

image

3 Likes

So I tried to use it CustomWait() instead of CustomWait(.1) on while true loop and my Computer hated me, should definitely add a default minimum

1 Like

…Could you elaborate? If no time is added, it will yield for 1 Stepped frame before unyielding shortly after.

Weird, I did this code and it exhausted the script

while true do
wait()
-- Code
end

That’s weird. I’ve confirmed this behavior just now, and it does seem like wait(0) or wait() achieve this effect. An easy solution would be to just do wait(0.01) for now. I’ll fix this next update and clamp the minimum value to 0.01.

1 Like

That doesn’t happen with my module. Seems like using a binary heap is not a good way of implementing custom yields.

Does your post have any other relevancy to this discussion other than to brag and advertise your own module?

1 Like

It isn’t bragging. I am just saying that your module isn’t the best solution to yielding (as it claims in the title), nor do I claim mine is the best.

Best != perfect. Don’t mix up the words. Thus far I have not seen any solution which other modules offer better solutions than this module. You mentioned your own module with no other relevancy other than to imply it is better than mine because the issue mentioned above does not occur with yours. Before you state that you didn’t say that, I meant imply. Intent is enough, and saying that was not your intent would be delusional.

1 Like