Regarding having a ton of RBXScriptConnections running at the same time

In the last 10 projects I’ve made, each one progressively getting more efficient and easier to develop than burning out because of how messy / bad everything is; I’ve almost always encountered an issue that I’ve been told not to care about and it didn’t look like it was breaking anything, but it really bothers me and I will provide some examples to explain what I’m trying to say better. (I’m not asking for improving the code here I’m asking a general question w examples)

Especially whenever I need a GUI of some sort with buttons on it related to “selecting” things, for every single instance of a button or clickable object that needs to do something if it is clicked, the code often goes something like this:

for thing, _ in pairs(Data) do
    local NewButton = Instance.new("ImageButton", GUI)
    -- ... configure the button design and all that
    NewButton.MouseButton1Click:Connect(function()
        -- Do stuff
    end
end

In the end, there is like tens maybe hundreds of buttons that all just have this connection.

And also whenever I have something like an Item or Cost that is displayed somewhere, there is always a .Changed or :GetPropertyChangedSignal assigned to every instance like this:

-- ... code that triggered getting an item or storing the item
local NewItem = ItemReference:Clone()
-- ... some sort of textlabel or billboardgui or tool description to tell you the amount
local IV = Instance.new("IntValue", NewItem) -- or SetAttribute
IV.Name = "Amount" -- Convenience
IV.Value = InitialAmount
IV.Changed:Connect(function(newAmount)
    ... .Text = ... newAmount
end

There are hundreds of items in your inventory or backpack or whatever GUI and every single one of them has this connection.

The idea here is kind of just looking for what can be done to tweak this to something else, if there is a way to do so. If there is no way around this and its just “how its supposed to be”, does this have any downsides or is there some threshold where this’d cause some lag or memory leak or whatever?

Here is an example of this happening in my actual code:

local function AddItem(newItem)
	-- Locate the newItem inside of GameData
	local ItemData = GameData.ItemsData[newItem.Name]
	local ItemType = ItemData.Type
	--print(ItemData.Name, ItemType)
	
	local InventoryMaxCap = CalculateInventory()
	-- Create new gui instance relating to it
	local newItemGUI = InventoryTakeoutButtonGUI:Clone()
	newItemGUI.Name = ItemData.Name
	newItemGUI.BackgroundColor3 = GameData.TypesData[ItemType].TypeColor
	
	-- this is the thing, you just do categories like JobItems are types FISH, FRAGS, ORES etc. all and the rest are .Visible = false'd when the code will run
	-- in the future but for now i dont care
	local itemColor = ItemData.ToolModel.Handle.Color
	local H,S,V = itemColor:ToHSV()
	newItemGUI.TextColor3 = itemColor
	newItemGUI.ContextualUIStroke.Color = Color3.fromHSV(H,S,V-0.3) -- i need to HSV this bro
	
	-- the text
	newItemGUI.Text = `[x{newItem.Value}] {ItemData.Name}` -- could use ItemData.Name or newItem.Name
	newItemGUI.Parent = MapInventory
	
	MapInventoryCapTitle.SurfaceGui.TextLabel.Text = `INVENTORY: {CalculateCurrentInventory()}/{CalculateInventory()}`
	
	newItem.Changed:Connect(function() -- its an integer value
		if newItem.Value > 0 then
			newItemGUI.Text = `[x{newItem.Value}] {ItemData.Name}`
			MapInventoryCapTitle.SurfaceGui.TextLabel.Text = `INVENTORY: {CalculateCurrentInventory()}/{CalculateInventory()}`
		else
			newItemGUI:Destroy()
			MapInventoryCapTitle.SurfaceGui.TextLabel.Text = `INVENTORY: {CalculateCurrentInventory()}/{CalculateInventory()}`
		end
	end)
	
	newItemGUI.MouseButton1Click:Connect(function()
		-- open the gui (This can tweaked to be more versatile later just open it dude)
		ItemTakeoutMenu.ItemName.Text = newItem.Name
		ItemTakeoutMenu.ItemName.TextColor3 = newItemGUI.TextColor3
		ItemTakeoutMenu.ItemShadow.Text = newItem.Name
		ItemTakeoutMenu.ItemShadow.TextColor3 = newItemGUI.ContextualUIStroke.Color
		
		local Request = ItemTakeoutMenu.Request -- textbox
		Request.Text = ""
		
		_G.OpenGUI(ItemTakeoutMenu)
		
		confirmConnection = ItemTakeoutMenu.ConfirmButton.MouseButton1Click:Connect(function()
			local numberRequest = tonumber(Request.Text)
			if (tonumber(Request.Text) and tonumber(Request.Text) % 1 == 0 and tonumber(Request.Text) > 0) or string.lower(Request.Text) == "inf" then

				if tonumber(Request.Text) < newItem.Value then
					ItemTakeoutEvent:FireServer(Player.Backpack, Character, newItem.Name, tonumber(Request.Text), newItem)
					
				elseif tonumber(Request.Text) >= newItem.Value then -- else works too but idc
					ItemTakeoutEvent:FireServer(Player.Backpack, Character, newItem.Name, newItem.Value, newItem)
					newItemGUI:Destroy()
				elseif string.lower(Request.Text) == "inf" then
					ItemTakeoutEvent:FireServer(Player.Backpack, Character, newItem.Name, newItem.Value, newItem)
					newItemGUI:Destroy()
				end

				_G.CloseGUI(ItemTakeoutMenu)
				Request.Text = ""

				confirmConnection:Disconnect()
			end
		end)
		
		cancelConnection = ItemTakeoutMenu.CancelButton.MouseButton1Click:Connect(function()
			
			_G.CloseGUI(ItemTakeoutMenu)
			Request.Text = ""

			cancelConnection:Disconnect()
		end)
		
		
		--newItemGUI.Name
	end)
end

There was a bug where my friend actually helped fix which was the confirmConnection and cancelConnection, which would start running multiple times everytime you’d Confirm and Cancel or do anything else. Every time you opened the menu again, clicked either button; It’d start running more and more of the same .MouseButton1Click since they were always there. Also sort of relevant to why I am asking this because these kind of things happen.

I assume any connection to a button or value will disconnect when that instance is destroyed, but what if they’re always there?

I don’t really know how to explain why I have this issue, every code till today worked using this and I didn’t encounter anything wrong with it, but it always bothered me that “Is this connection just always there checking for something to happen when 99% of the time nothing is happening to most of the things?”

There’s nothing wrong with having a lot of event connections whatsoever. The only thing you should be concerned about is the cleanup of said connections and if you’re doing so properly. The above code will memory leak due to how you’re utilizing connections unless you disconnect cancelConnection and confirmConnection else where that I don’t see. There’s definitely better ways you could go about structuring this code, it’s just going to be hard for me to give you any clear suggestions without fully understanding what you’re trying to achieve.

Im trying to understand if there is any downsides to it and yeah, you could summarize my problem to “how do you properly do the cleanup of this sort of thing?” I don’t quite understand how you find or deal with memory leaks or lag caused by something, and some games I made randomly starts lagging in weird patterns and I cannot find what is causing the issue, my only insight is this lingering suspicion of these connections.

I guess you could say what I’m looking for is how to gracefully handle connections without causing any lag or memory leaks and if there is any situation where I’d need to avoid them. PS: This entire suspicion is also relevant whenever I’m using coroutines and some loops but I don’t really have an example for this other than this code:

local function Increment(Player, StatType, Amount, Time, InstanceSelf : Model)
	local KeepGoing = true
	InstanceSelf.Destroying:Connect(function()
		KeepGoing = false
	end)
	
	task.spawn(function()
		while task.wait(Time / InstanceSelf:FindFirstChild("SpeedDivBoost").Value) and KeepGoing do -- if the instance gets deleted instanceself is false so
			local Things = Player:GetDescendants()
			for _, Thing in ipairs(Things) do
				if Thing:IsA("IntValue") and Thing.Name == StatType then
					Thing.Value += Amount
				end
			end
		end
	end)
end
-- ...
	if UnitData.Generation then
		local MultiplicationBoost = Instance.new("IntValue", UnitModel)
		-- this is for later it will be set to 1 and the task.spawn can use this to refer and other things can change this and the ones below
		local SpeedDivBoost = Instance.new("IntValue", UnitModel)
		SpeedDivBoost.Value = 1
		SpeedDivBoost.Name = "SpeedDivBoost"

		for StatType, GenData in pairs(UnitData.Generation) do
			--print(StatType, GenData)
			Increment(PlayerInstance, StatType, GenData.Amount, GenData.Cooldown, UnitModel)
		end
	
	end
-- ...

Where you place down some things on a grid and they all begin their own loop of generating stats, which with like 50 of these down means 50 loops running at the same time. I don’t know what else I could’ve done to achieve something similar but this seems extremely inefficient or bound to cause a leak or lag, and I don’t know what could be wrong?

You’ll need to utilize both the LuauHeap and MicroProfiler in order to identify what’s causing issues in your game. There’s other things to look at as well but, these are by far the ones you SHOULD know how to navigate.

LuauHeap will help you find where increases of memory are coming from through snap shot comparisons. The MicroProfiler will help you identify frame to frame performance issues.

Not trying to take a jab at you or anything but, the only way to gracefully handle connections is by improving overall code structure and how you approach certain things. Most of my early projects ended up becoming too much to handle due to poor structure. If you’re looking for ways to handle cleanup of things, there’s also some resources you can use such as Janitor(see more here).

Its totally fine, that’s why I keep making new projects as I improve, I don’t get stuck on one project for longer than 2 weeks~ because I’m trying my best to teach myself everything.

Thanks for the resources, I don’t know how using MicroProfiler didn’t cross my mind before even though I use it everyday :rofl:

I guess that means if I really have to, there is no problem using a lot of connections and loops as long as they’re properly cleaned up and you prevent memory leaks. Thanks again!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.