Is this signal handleling approach valid?

So, I wanted a way to receive signals from a lot of different scripts without creating a bunch of connections and this is what came up with:

local signal_table = {}

local function RegisterNewSignal(signal: RBXScriptSignal)
	signal_table[signal] = {}
	
	signal:Connect(function(...)
		for _, func in pairs(signal_table[signal]) do
			
			local call = coroutine.wrap(func)
			call(...)
			
		end	
	end)
end

local module = {}


function module:SignTo(signal: RBXScriptSignal, _function: (any) -> ())	
	if not signal_table[signal] then
		RegisterNewSignal(signal)
	end
	
	local key = #signal_table[signal]+1
	signal_table[signal][key] = _function
	
	return key
end

function module:SignOff(signal: RBXScriptSignal, key: number)
	if signal_table[signal] then
		signal_table[signal][key] = nil
	end
end

return module

Is there any problem with this system? Would it just be better to connect directly to the signal? Is there anything I should change to improve it?

1 Like

It looks great, but there are few details that could be adjusted:

  • Using coroutines for every callback can be overkill, especially if the callbacks are simple and don’t require yielding.
  • Keeping references to functions in signal_table can prevent them from being garbage-collected if they’re not needed anymore.
  • I mean, it’s not necessary, but I love adding error handling things so I would suggest considering adding error handling to avoid issues that could stop other callbacks from being executed, like pcall or something like that.

Those things are not necessary, because your module is already pretty clean, but those “advices” will improve it’s memory management and performance in big projects.

1 Like

Thanks! I’m using coroutines because I want all the functions signed to the signal to run pretty much at the same time (like if they were connected to the original event!). Going to be implementing some more sanity checks on it.

1 Like

If you find that helpful, I’d appreciate if you mark solution to it.

Hey, just wanted to clarify something here, but what your module is doing is a trade off between performance and memory. Technically what you’re doing will result in less memory but will take longer to execute (especially in cases where many 100s of connections are happening on one remote). The memory gains are not sufficient enough as a tradeoff for performance in my experience.
Essentially this module is what Connect already does but internally. The only reason why it would result in more memory is because of the objects it creates for each connection.

tl;dr: Yes, it’s practically better to just connect directly instead of wrapping it like this.

1 Like

I wasn’t really planning on making 100s of connections, but yeah you’re right. After doing some more testing I realized this system wasn’t very practical and I was better off just using remote events to handle communication between local scripts. I guess what Im looking for is a good way to share variables beetween local scripts without having to use a bunch of events.