Custom wait - the best solution to yielding!

This module or task.wait? better for accuracy?

4 Likes

This module is still ever-so-slightly more accurate, as it doesn’t need to converse back and forth between the Lua and C APIs unlike task.wait.
Sorry for late response, I don’t usually check up on this thread.

1 Like

Thanks for this module. I did some tests myself and found that @StrategicPlayZ’s wait module is ever so slightly better.

1 Like

Made a pull request which replaces the old junk coroutine.resume in favour of the new and better task.spawn. Ideally this module should be using the new task library.

1 Like

Thanks, I merged the commit. Also made additional changes to casing, variable names, and additional localization of table.insert.

My code for one of my modules seems to yield permemently and I have no idea why.

Code Source
local FuncE = function(CPM:number|()->(number),keepCache:boolean)
		local sV = {}
		local function pCPM():number
			if typeof(CPM) == 'function' then
				return CPM()
			else
				return CPM
			end
		end
		while true do
		data.OnYield:Connect(function()
			data['OnResume'].Event:Wait()
		end)
			if #data['_D']['Sched']>0 then
			sV[3],sV[4]=pcall(data['_D']['Sched'][1]['Func'],table.unpack(data['_D']['Sched'][1]['Args'])) -- [pcall's the function with the given arguements]
				if sV[3] then data['OnUpdate']:Fire() else data['OnError']:Fire(sV[4]) end -- [Fires the 'error' event if an error is returned]
			if keepCache then table.insert(data['_C'],data['_D']['Sched'][1]) end -- [Keeps cache]
			data['_D']['Sched'][1] = nil -- [Cleares the called schedule]
			if #data['_D']['Sched'] == 0 then -- [Check if there are any functions in the sequence]
				sV[1] = os.clock()
				data['OnAddedSched']:Wait()
				sV[2] = os.clock();if sV[2]-sV[1] < CPM then cwait(sV[2]-sV[1]) end -- [Delays if needed to not exceed CPM]
			end
			end
			sV[5] = 60/pCPM()
			cwait(sV[5]) 
		end
	end	

The function is run in a coroutine. Although it still would infinitely yield w/ or w/o a coroutine.

@PysephDEV still having an issue with this infinitely yielding in loops w/ or w/o coroutines.

Please provide a minimal repro, it’s really hard to make out what’s going on with all that code.

cwait(), which is your custom wait function, is called with a number after the code for the loop has been done, but it would yield infinitely in that loop

Here's the code again but with relevant parts only
local FuncE = function(CPM:number|(number)->(number),keepCache:boolean)
		local function pCPM():number
			if typeof(CPM) == 'function' then
				return 60/(sV[13]-(OsClock()-sV[12]))	
			else
				return 60/CPM
			end
		end
                 --main code here
	        cwait(pCPM())
		end
	end	

This is still very convoluted and bloated with behavior I’m unaware of. sV is not defined, CPM is a variable and not a constant, pCPM should be removed and replaced with a constant variable, etc. There can be a lot of factors at play here, such as (sV[13]-(OsClock()-sV[12])) being lesser than 1, or perhaps approaching 0, which would lead to the yield being seemingly “infinite”, when in reality it simply yields for absurd periods of time.

sV is just a table for variables that aren’t in much use to have their own local variable. sV[13]=()sClock()sV[12])) will always be above 0. And for the inputs it was a number anyways and so it should be fine.

pCPM is for processing CPM, which can either be a function that takes in a number then outputs it, or just a number for the wait.

That’s not what I meant.

Again, even if you were to explain the functionality behind that block of code, it can still be made into more minimal. It still has unnecessary code as of now.

Hold on, have I got an older version? Even this simple code in command bar yields infinitely.

print('a') cwait(2) print('b')
Source for the version I got
local OsClock = os.clock
local CoroutineYield = coroutine.yield
local CoroutineRunning = coroutine.running
local TaskSpawn = task.spawn
local TableInsert = table.insert

-- Pre-allocate 100 indices
local Yields = table.create(100)
game:GetService("RunService").Stepped:Connect(function()
	local Now = OsClock()
	for Index, Data in next, Yields do
		local TimeYielded = Now - Data[1]
		if TimeYielded >= Data[2] then
			Yields[Index] = nil
			TaskSpawn(Data[3], TimeYielded, Now)
		end
	end
end)

return function(YieldTime)
	YieldTime = (type(YieldTime) ~= "number" or YieldTime < 0) and 0 or YieldTime
	TableInsert(Yields, {OsClock(), YieldTime, CoroutineRunning()})
	return CoroutineYield()
end

The reason it doesn’t run in the command bar is because the module uses Stepped, which does not fire if you’re not running a game. Try putting that same code in a script and run the game, and it will work.

1 Like

That makes sense. I have been testing in command bar so that’s most likely my issue. Thanks for the help!

1 Like