Wait function alternative?

Hello, i made this quick implementation for the wait function and i wonder if it’s better than the global one, Here is my code:

function wait_time(duration)
    local start = tick()
    local Heartbeat = game:GetService("RunService").Heartbeat
    repeat Heartbeat:Wait() until (tick() - start) >= duration
    return (tick() - start)
end

The function is a replacement for the wait function but more precise and effective.
If 0 passed as the duration, it will yield until the next frame.
it returns the exact time the function yielded for more precise calculations.
One line version:
function wait_time(d)local s=tick()local h=game:GetService("RunService").Heartbeat repeat h:Wait()until(tick()-s)>=d return(tick()-s)end

2 Likes

You could use Bindables too, instead of running algorithms after every frame.

1 Like

How bindables will help in this case?

Your implementation is alright,

I was suggesting Bindables can be used in cases where you could avoid wait(n) for instance like:

 local yield = Instance.new("BindableEvent")
 local waited
 
 if not waited then
    yield.Event:Wait()
 end
 -- and you could do yield:Fire() right before some function returns
1 Like

Well in this case if i want to trigger an actual action. but what if i just want to yield the thread until a specific time have passed.
I don’t see how this can be used, or maybe am missing something here

1 Like

It still calls a yielding function, :Wait(), and will run into many of the same issues as wait() would.

5 Likes

It would be better to instead connect to RunService.Heartbeat then, as long as the listener doesn’t yield the thread would be reused, and it will run every frame then.

 local runService = game:GetService("RunService")
 
 local function Wait(n)
 n = n or 1/60
 n = math.clamp(n, 1/60, math.huge) 

 local completed, t = Instance.new("BindableEvent"), tick()

 local connection = runService.Heartbeat:Connect(function()
 if tick() - t >= n then 
     completed:Fire()         
    end
 end)

    completed.Event:Connect(function() 
        connection:Disconnect()
    end)
 
    completed.Event:Wait()
    return tick() - t
 end


 Wait(5)
 print("hello")

This way it uses :Wait(), but still had more accuracy than wait(n) over several calls.

Each time a similar result was lol yielded:

image

1 Like

Refer to Avoiding wait() and why for an in-depth exploration of why :Wait() is often times greater than wait().

However, you really should be avoiding the use of polling in your project, @IlyasTawawe; instead, look into event-based programming as a paradigm and, in nearly any place possible, avoid replicating wait or some derivative of it when you’re waiting for less than or equal to 1/30th of a second.

1 Like

Though admittedly better, :Wait() is still unreliable and may defer. A coroutine-based solution where you explicitly resume the thread is likely to be more reliable/consistent. This solution is fine as is, but :Wait() does still have issues. Here’s an explanation written up by @evaera that does a better job explaining this than I can: (not my screenshot)

8 Likes

This function will be slightly more accurate, albeit a very minuscule change. However, you won’t need to yield a frame when duration is 0.

local function wait_time(duration)
    local start = tick()
	while tick() - start < duration do end
    return (tick() - start)
end

Also, a small nitpick, if you want to use your function as the built-in wait function, you may want to add a check for duration if you want to use your wait function as wait_time(0)

if not duration then duration = 0
1 Like

But this will crash the server for greater values because threads need to yield back to the engine or they time out.

A better approach would still be to either use Heartbeat:Wait() or connect to a listener not yielding within, that checks whether a certain time has elapsed and returns if has.

1 Like

Well my implementation is really minimal and optimized.
I used Heartbeat, even tho it’s slower but it’s more performant than stepped roughly 3 times on my machine.
So i guess my implementation is fine for now. Also i didn’t do any side checks like for duration just to make sure the function is minimal.
For better accuracy, i don’t think making a bindable event will help, especially if this function is called every frame will create an event, listen to it and disconnect it.
For more accuracy, i suggest trying to use Stepped or even RenderStepped instead in place of Heartbeat.
Again anyone can correct me here!
Also i always use Events when possible, but in the case where i want to strictly just yield for a specified amount of time, i don’t have any other way around, like Debounce for example?!

The new implementation:

function wait_time(duration)
    local start = tick()
	duration = duration or 0
    local Heartbeat = game:GetService("RunService").Heartbeat
    repeat Heartbeat:Wait() until (tick() - start) >= duration
    return (tick() - start)
end

One line version:

function wait_time(d) local s=tick()d=d or 0 local h=game:GetService("RunService").Heartbeat repeat h:Wait()until(tick()-s)>=d return(tick()-s)end

Also through the topic that @TheEdgyDev mentionned, here is what i found


Other alternative:

function wait_time(duration)
local counter = 0
duration = duration or 0
local Heartbeat = game:GetService("RunService").Heartbeat
repeat counter += Heartbeat:Wait() until counter >= duration
return counter
end
1 Like