Issue with GetAttributeChangedSignal for Deferred SignalBehavior

I have a sample code pattern here that’s causing issues in my development. How do I work around this? I want it to have the behavior of intermediate mode for this and NOT the deferred mode. The issue is GetAttributeChangedSignal does NOT pass any parameters in its event bind so I have to :GetAttribute() inside the connection function.

Should I just switch to intermediate or is there a standard workaround for this?

Problem code.

workspace:GetAttributeChangedSignal("TestValue"):Connect(function(...)
	print("workspace changed!", workspace:GetAttribute("TestValue"), ...)
end)

workspace:SetAttribute("TestValue", 1)
workspace:SetAttribute("TestValue", "hi")
workspace:SetAttribute("TestValue", true)
workspace:SetAttribute("TestValue", false)
workspace:SetAttribute("TestValue", nil)
workspace:SetAttribute("TestValue", CFrame.new())

--[[
	Print Output:
		Deferred:
			workspace changed! 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 (x6)
			
		Immediate:
			workspace changed! 1
			workspace changed! hi
			workspace changed! true
			workspace changed! false
			workspace changed! nil
			workspace changed! 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1
--]]
3 Likes

Not with attribute changes. I’m assuming you care about the values in each change rather than the handler running extra times with the same value. You can mix the attribute with a BindableEvent or Lua signal library so that it provides the new value. To change the attribute, fire the signal with the new value. An extra handler can set the attribute itself or you can do it before the signal firing if the handler might try to read the attribute itself (could be a stale old value until that previously mentioned handler sets it). I understand if an extra Instance or library integration seems convoluted, but events are more suited for separate occurrences of data than value holders like attributes.

2 Likes

Defered signal behavior has nothing to do with that
It is simply how GetAttributeChangedSignal works
It doesnt return any value inside connection: Instance | Documentation - Roblox Creator Hub
It returns a RBXScriptSignal that to which you can connect events.

I believe the issue for @treebee63 is the fact that they’re not able to run callbacks for each value change. Deferred signal behavior is the reason that happens as all event callbacks get pushed to a later time to be executed.

They’re wondering if there’s way to continue using deferred signal behavior but somehow be able to have a callback run for each value change, or if it’s better to just switch to intermediate mode.

1 Like

oh in this case it will be to avoid attributes entirely and rely on manual calls.
Also no defered engine while does execute everything in defered mode it doesnt cancel other signals.

1 Like

You can’t change the behavior for this case. Deferred mode simply locks you out of this because the callback is designed to never run in-between each :SetAttribute() call (LIFO rule).

I’m also assuming you know about AncestroyDeferred, which I guess is not suitable for your case.

May I ask what exact pattern of yours is breaking? If you’re determined on using Deferred mode, maybe you should find a different way to structure how you handle attributes.

1 Like
game.ReplicatedStorage.Services.Game.Grab.Event:Connect(function(player: Player, targetTreasure: Model)
	if not player or not AIHELPER:CharAlive(player.Character) then return end

	module:DisconnectGrab(targetTreasure)
	targetTreasure:SetAttribute("GrabbedBy", player and player.Name)
end)

function module:DisconnectGrab(treasure: Model)
	local attachment = module.BindedTreasures[treasure]

	if attachment then
		module.BindedAttachment[attachment] = nil
	end
	module.BindedTreasures[treasure] = nil
	
	treasure:SetAttribute("GrabbedBy", nil)
end

treasure:GetAttributeChangedSignal("GrabbedBy"):Connect(function()
	...
end)

Issue: when Game.Grab:Fire(player, treasure) is called, :SetAttribute(“GrabbedBy”) is called twice by design, leading to the 1st call value being dropped.

I call DisconnectGrab which sets GrabbedBy to nil first (used to passively wipe the object states clean and prevent previous changes from leaking over). Then I assign the player’s name to GrabbedBy to initiate the GrabbedBy event.

The GetAttributeChangedSignal method is very useful. I can watch the value independently across different scripts. This lets me have scripts to draw the view (clientside) and scripts to manipulate the model (serverside). Bindable/Remote events can do something similar but are more convoluted to work with. Signals cannot communicate across independent scripts.

I think at this point I’ll just restructure the code to a different system. It doesn’t seem like there’s any easy way to do this now.

1 Like

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