ImmediateRenderStepped

ImmediateRenderStepped

Source


ImmediateRenderStepped is a custom event for RenderStepped which uses :BindToRenderStep so that you can use an event which behaves a similar way as RenderStepped behaved before Deferred events were a thing.

Before deferred events, any RenderStepped connections that updated anything would be run before the frame was actually rendered, therefore it made things easier, and more elegant, as you didn’t need to use :BindToRenderStep for that behaviour to work that way.
However, with deferred events, only :BindToRenderStep connections run immediately.

We can use that to our advantage!

This is what this small library does, it gives you a custom RenderStepped event which is based of :BindToRenderStep, which runs code immediately using task.spawn.
ImmediateRenderStepped also implements routine recycling from GoodSignal!

8 Likes

also can be simplified down to 28 lines out of the original 128 lines

local ImmediateRS = {Connections = {}}
local Connection = {}
Connection.__index = Connection

local RunService = game:GetService("RunService")

function Connection:Connect(func)
	local self = setmetatable({},Connection)
	
	self.Index = #ImmediateRS.Connections + 1
	table.insert(ImmediateRS.Connections,func)
	
	return self
end

function Connection:Disconnect(Guid)
	table.remove(ImmediateRS.Connections,self.Index)
	self = nil
end

RunService:BindToRenderStep("ImmediateRS",Enum.RenderPriority.Last.Value,function(...)
	for _,Connection in pairs(ImmediateRS.Connections) do
		Connection(...)
	end
end)

return Connection

Using arrays for signal connections is bad.
Especially if you’re using Immediate signals.
Depending on what you :Disconnect in a connection, it can completely stop the rest of the connections from firing.
As I want an actual signal, that’s close to actual ScriptSignal behaviour, your implemention, not a good idea.

Also, your solution while not using multiple routines, does not support yielding functions. People using your code would be annoying you all the time for it.

Order on your solution is (last connected to first connected) which has some problems, new connections that were connected while the event is being fired, will also be fired on that same fire session.

So, some of the issues I can think of at the moment, even if there are probably more, are:

  • Your :Disconnect will not work properly either.
  • No :Wait method.
  • No support for yielding code (this is a library, it’s meant to be used by multiple people and use cases, even if weird ones)
  • Can fail and not fire all connections it should
  • Functions connected on another connection handler will be fired at the same fire session, not expected behaviour

:face_with_raised_eyebrow:
If you’re gonna say something like that, at minimum you need to show what you’re considering “boilerplate code”.

1 Like

how? i understand other functions stopping other functions from running because of them taking a while to do their own thing, but how is disconnecting gonna stop the whole cycle?

i’ve actually wanted to add support for yielding but later didnt find the need for it

pairs goes up from the first index, doesn’t it? or am i misunderstanding something?
turns out it doesnt, error comes out last too:


im not too experienced in this type of stuff, only really written loops that don’t rely on the order of things.

missed this part, thanks for that

ipairs will always go in order if it isn’t a dictionary, pairs doesn’t guarantee perfect order

reason ipairs doesn’t work on dictionaries is because they have no order, #Dictionary = 0

1 Like

The problem isn’t that order, it’s the order on which they’re fired or connected.

Pairs does respect array order if the array part is correctly ordered etc.

ScriptSignals connections fire from last function connected to first function connected. That’s the problem I was referencing.

This code should show that problem on your solution, unless I’m missing something.

local connection = nil
connection = RenderStepped:Connect(function()
    connection:Disconnect()

    print "this should print"
end)

RenderStepped:Connect(function()
    print "this won't run the first time RenderStepped fires"

    --// this was NEVER disconnected, therefore it should have fired.
end)

That’s because pairs/ipairs works by basically checking what comes after the last index it found. If that index is non existent anymore, it will think all indexes were looped through.

In the code above, any connections after the second one should run just fine, but when you’re working with multiple connections, disconnecting and connecting again and again, it is totally possible for it to not fire any other functions properly.

can’t really replicate this, moment i disconnect pass2 everything still runs:
image

local ImmediateRS = require(game:GetService("ReplicatedStorage"):WaitForChild("ImmediateRS"))
local Pass = ImmediateRS:Connect(function(dt)
	print("This will pass")
end)
local Pass2 = ImmediateRS:Connect(function()
	print(';lol')
end)
local Pass3 = ImmediateRS:Connect(function()
	print("Reached i think ??")
end)


wait(6)
Pass2:Disconnect()

I was only explaining how ipairs and pairs works
sorry about that

I was talking about dictionaries, not arrays

Update (kind of)


Roblox LSP should now give you a hint of the DeltaTime parameter when calling :Connect!
Make sure to update Roblox LSP to the newest version.

4GgOHSbQI3

RenderStepped is an invocation point, meaning it is completely unaffected by Deferred signal behaviour.

With deferred signal behaviour:

With immediate signal behaviour:

Code tested:

game:GetService("RunService").RenderStepped:Connect(function(dt)
	print(dt)
end)
1 Like