Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
COMMUNITY QUESTION

[SOLVED] How to run a function once in Object-Oriented Programming?

Hello. I'm trying to make my own Custom Signal module without using BindableEvents nor using a module from the DevForum. But I don't know how to make the handler Signal:Once() which basically uses Connect but only does it once. I tried using coroutines, debounce, and setting the function to nil, but it still runs more than once like Signal:Connect() and not like RBXScriptSignal:Once(). Here's the module for reference:

local module = {}
module.__index = module

local backupBindableEvent = Instance.new("BindableEvent")

function module.new(signalName: string)
	local self = setmetatable({}, module)
	local newSignal = {}
	
	local newSignalName: string
	
	if signalName ~= nil then
		newSignalName = signalName
	else
		newSignalName = "Event"
	end
	
	self._listeners = require(script.Listeners).new()
	local listenersModule = self._listeners
	
	function newSignal:Connect(func)
		listenersModule:AddListener(func)
		
		return {
			Connected = true,
			Disconnect = function(self)
				listenersModule:RemoveListener(func)
				self.Connected = false
			end
		}
	end
	
	function newSignal:ConnectParallel(func)
		task.desynchronize()
		listenersModule:AddListener(func)
		return {
			Connected = true,
			Disconnect = function(self)
				listenersModule:RemoveListener(func)
				self.Connected = false
			end
		}
	end
	
	function newSignal:Once(func)
		return backupBindableEvent.Event:Once(func) -- using BindableEvents until i figured it out!
	end
	
	function newSignal:Wait()
		return backupBindableEvent.Event:Wait() -- using BindableEvents until i figured it out!
	end
	
	self[newSignalName] = newSignal
	
	return self
end

function module:Fire(...: any)
	local paramsTable = table.pack(...)
	
	backupBindableEvent:Fire(table.unpack(paramsTable))
	
	task.defer(function()
		local listenersModule = self._listeners
		
		repeat task.wait() until listenersModule:GetListenersAsync() ~= nil
		local listenersTable = listenersModule:GetListenersAsync()
		
		if listenersTable ~= nil then
			for _, listenerFunc in pairs(listenersTable) do
				task.defer(function()
					listenerFunc(table.unpack(paramsTable))
				end)
			end
		end
	end)
end

return module

Also I don't know how to make the Signal:Wait() function. Right now, I'm temporarily using BindableEvents to make those functions work.

And also, if you noticed anything wrong in my module that is off-topic, lemme know!

1 comment

0

Wait will be almost the same as :Once but at the end instead of returning, do a repeat wait() until not myTable.Connected

1 answer

It's hard to help here because this is highly dependent on the listenersModule you have - Personally, I would add an :AddOnce(func) in addition to :AddListener(func) and let that module handle it, but this also runs into the issue of the Connected variable being different from reality.

Easiest way with given code is:

function newSignal:Once(func)
	local myTable = {Connected = true}
	local function singleUse()
		myTable:Disconnect()
		func()
	end
	listenersModule:AddListener(singleUse)
	function myTable:Disconnect()
		listenersModule:RemoveListener(singleUse)
		myTable.Connected = false
	end
	return myTable
end

4 comments

0

The listenersModule has 3 functions: :AddListener(), :RemoveListener(), and :GetListenersAsync(). The listenersModule just simply creates an array of functions

0

I also need help in making the :Wait() function. Thanks!

0

But sure, I’ll try to make an :AddOnce() function.

0

Sadly, yours did not work but I finally solved the :Once() function. I just converted the listener to a thread, and closed it once it was fired to avoid of doing it again.

This discussion is preserved as it was. New answers, comments, and votes are closed.