SimpleSignal - Another signal implementation that doesn't use metatable

To use this module, copy this code below:

local freeThreads: { thread } = {}

local function runCallback<T...>(callback: (T...) -> (), thread: thread, ...: T...)
	callback(...)
	table.insert(freeThreads, thread)
end

local function yielder()
	while true do
		runCallback(coroutine.yield())
	end
end

local function spawn<T...>(callback: (T...) -> (), ...: T...)
	local thread: thread

	if #freeThreads > 0 then
		thread = freeThreads[#freeThreads]
		freeThreads[#freeThreads] = nil
	else
		thread = coroutine.create(yielder)
		coroutine.resume(thread)
	end

	task.spawn(thread, callback, thread, ...)
end

type SignalNode<T...> = {
	callback: (T...) -> (),
	next: SignalNode<T...>?,
}

type SignalData<T...> = {
	root: SignalNode<T...>?,
}

local function disconnect<T...>(self: SignalData<T...>, node: SignalNode<T...>)
	if self.root == node then
		self.root = node.next
	else
		local current = self.root

		while current do
			if current.next == node then
				current.next = node.next
				break
			end

			current = current.next
		end
	end
end

export type Signal<T...> = {
	connect: (callback: (T...) -> ()) -> () -> (),
	disconnectAll: () -> (),
	fire: (T...) -> (),
	once: (callback: (T...) -> ()) -> () -> (),
	wait: () -> T...,
}

local function Signal<T...>(): Signal<T...>
	local self: SignalData<T...> = {}

	local function connect(callback: (T...) -> ()): () -> ()
		local node: SignalNode<T...> = {
			callback = callback,
			next = self.root,
		}

		self.root = node

		return function()
			disconnect(self, node)
		end
	end

	local function disconnectAll()
		self.root = nil
	end

	local function fire(...: T...)
		local current = self.root

		while current do
			spawn(current.callback, ...)
			current = current.next
		end
	end

	local function once(callback: (T...) -> ()): () -> ()
		local unsubscribe: () -> ()

		unsubscribe = connect(function(...)
			unsubscribe()
			callback(...)
		end)

		return unsubscribe
	end

	local function wait(): T...
		local thread = coroutine.running()

		once(function(...)
			task.spawn(thread, ...)
		end)

		return coroutine.yield()
	end

	return {
		connect = connect,
		disconnectAll = disconnectAll,
		fire = fire,
		once = once,
		wait = wait,
	}
end

return Signal

Why SimpleSignal?:

  • Faster method call, why call : instead of .?
  • Explicit type check.
  • No more ugly intellisense like __index

doesnt simpleSignal already exist

2 SimpleSignals already exist fwik