Is .Changed efficient?

I’m currently working on a system that involves me checking if an instance were to change through the .Changed value. I’m wondering if it’s efficient to do it that way, does changed pretty much loop to check if the value is not equal to the pre-existing one every second? If so, would making one’s own while loop and setting a higher time be more efficient?

I didn’t know if this belonged on this category or not, since it might be a little bit opinion-based.

1 Like

If you are only looking for changes in one specific property, it is recommended you use Instance:GetPropertyChangedSignal(string propertyName). You can see the Developer Hub page for it here.

If you are looking for changes in any property of an instance, Instance.Changed should work fine.

3 Likes

.Changed should work fine if you’re watching for multiple property changes at once, if you just want to watch a single property change then you should use :GetPropertyChangedSignal() @joritochip mentioned.

However, if you’re watching for physics related property changes on BaseParts like Position and Orientation, neither .Changed nor :GetPropertyChangedSignal() would work out for you because neither of these options fire when a physics related property change and you should connect a function to RunService.Stepped event to constantly check if physics related property changed instead.

If you’re using a ValueBase object like StringValue or NumberValue then you don’t need to use :GetPropertyChangedSignal() to listen to Value property change because ValueBase objects use a modified .Changed event that only fires when the Value property changes. Unless you’re trying to another property change of ValueBase object then yeah you should use :GetPropertyChangedSignal() on that property.

6 Likes

dependent on the circumstances,

Its good to use if

  • you are ok that you can’t exactly control when this happens (since this is built and going to use roblox’s event structure you are at the whims of when it decides to update that event)
    – make sure to have an early termination check so that if you don’t need to run logic you can bail out of the event fast. This does matter, as remeber these events and the event system in roblox is going to be processing them in some order. One must complete before the next can happen

If you need a hyper reliable controlled or reasonable assured instant response on changed. You should create a function call within any thing that changes that variable that calls something like in a module that you can listen for.

1 Like

If I were to utilize it on a folder, would I be able to check if any of its children changed? In other words, does the Changed event check if the children are being changed?

Yes on child added and and on child removed are the two event hooks for that.

They will also pass through the object to the function you link it too.

1 Like

No, but you can use Instance.ChildAdded and Instance.ChildRemoved to accomplish this.

1 Like

example

‘’’
local folder = workspace.SomeFolder

 folder.OnChildAdded:Connect(function(Obj)
      print(Obj.Name)
end)

‘’’

1 Like

I’m a bit confused, would that let me know how if the object has changed in any way? Wouldn’t that just detect if it’s being added? I just don’t want a changed function for every single one of my stats / status effects.

Would there be a way to locate if any change happened to the children of the folder and then find out specifically what caused it? Or would that consume just as much memory

You would have to watch .Changed event on all of them which you can do with a simple loop.

1 Like

I created a script for you that should allow you to detect any child added, child removed, and child property changed. It does memory management by clearing up Changed events for items no longer in the folder. Feel free to ask questions if you don’t understand what it’s doing, but I did try to comment it.

local Folder = script.Parent
local Connections = {}

local function ChildAdded(Object)
	Connections[Object] = Object.Changed:Connect(function(Property)
		-- Property changed on Object, new value is Object.Property
		
		-- If you want to run more code when a property on a child is changed, do it here
	end)

	-- If you want to run more code when a child is added, do it here
end

-- Handle any existing children before script ran
for _, Child in pairs(Folder:GetChildren()) do
	ChildAdded(Child)
end

-- Handle new children 
Folder.ChildAdded:Connect(ChildAdded)

-- Clear connections (for memory purposes)
Folder.ChildRemoved:Connect(function(Object)
	if Connections[Object] then
		-- This will stop looking for any changes to the object.
		
		-- If the object was destroyed, this happens automatically, 
		-- but if it was moved, we need to do it ourselves.
		Connections[Object]:Disconnect()
	end
	
	-- If you want to run more code when a child is removed, do it here
end)
6 Likes

I’ll be tweaking this to try and make it work for my own project, thank you for the advice, I’ll rewrite it so I can understand it myself to its fullest, I wasn’t planning on making any child despawn, but I now understand how I can use the changed event on a folder’s children

1 Like

Like others have said i’d recommend using :GetPropertyChangedSignal() instead of .Changed if you’re only scanning one property. If you’re doing multiple then .Changed is fine. Another method is simply loop checking if the value has changed using RunService or a wait() loop but this is usually ineffective. I’d only recommend doing that if you’re trying to trick exploiters or something along those lines. If you want to check for instances being removed or added you can use ChildAdded and ChildRemoved. If it’s descendants then use DescendantAdded and DescendantRemoving. Again, simply looping would work using :FindFirstChild but it’s ineffective unless you’re trying to fool people for whatever reason.

1 Like

One more thing a catch about child added and removed.

There are race conditions that can cause these to miss the event generally these happen. If something is done or happens during or before the connection has happened

You can also poll the folder and cache check changes.

– create a lazy loop
– that loop peers into folder to see if anything is new in there or has changed
– store a dictionary ref like. Dict[instance] = instance
Then when it searches the folder you just grab the current children and see if they all have a ref in the dictionary if not then you have something new and add it and you can proc a function to happen.
– after that run the loop again this time using the dictionary as the check anything in the dictionary that can’t be found in the folder is no longer there removed event link

Pro tip cheat.

Store the value of the folder like
Local foldersize
Local lastfoldersize

Do this at the start of the poll
foldersize =#folder:GetChildre()

At the end of the poll
lastfoldersize = foldersize

Compare the last folder val to curent if greator an add event happened and you can run your checks to find out which thing was added if its less then last time it polled something removed and you can run different checks to see what got removed and updating and function procing accordingly

2 Likes

This is all running on the server so I don’t think an exploiter will have access to it. Hopefully. :skull:

Yeah in that case just use :GetPropertyChangedSignal or .Changed. Also, one thing that should be self explanatory is to be sure not to run a loop if you’re using these functions. They already loop themselves and adding a second loop would simply be useless and (correct me if I am wrong) will add a small performance impact.

1 Like

I wold personally, use a maid service for this, I have made one.
Code:

local lib = {}

local MakeMaid do
	local index = {
		GiveTask = function(self, task)
			local n = #self.Tasks+1
			self.Tasks[n] = task
			return n
		end;
		DoCleaning = function(self)
			local tasks = self.Tasks
			for name,task in pairs(tasks) do
				if type(task) == 'function' then
					task()
				elseif typeof(task) == 'RBXScriptConnection' then
					task:Disconnect()
				else
					task:Destroy()
				end
				tasks[name] = nil
			end
		end;
	};
	index.Destroy = index.DoCleaning -- Allow maids to be stacked.

	local mt = {
		__index = function(self, k)
			if index[k] then
				return index[k]
			else
				return self.Tasks[k]
			end
		end;
		__newindex = function(self, k, v)
			local tasks = self.Tasks
			if v == nil then
				-- disconnect if the task is an event
				if type(tasks[k]) ~= 'function' and tasks[k] then
					if typeof(tasks[k]) == 'RBXScriptConnection' then
						tasks[k]:disconnect()
					else
						tasks[k]:Destroy()
					end
				end
			elseif tasks[k] then
				-- clear previous task
				self[k] = nil
			end
			tasks[k] = v
		end;
	}

	function MakeMaid()
		return setmetatable({Tasks={},Instances={}},mt)
	end
end

lib.MakeMaid = MakeMaid
lib.new = MakeMaid

return lib
1 Like

I likely would have used maids also, but I figured it would be a bad idea to introduce complex stuff like that in this context.

Typically you should help people at their skill level so that they can grasp concepts before learning more complex things.