Unfortunately we actually have to move to a world where there is no setting and events are always deferred before we can reap most of the benefits.
That’s because even though some people have switched to deferred mode, the C++ code still has to be written in the slower way which is capable of handling interruption by immediate mode events while the setting exists. (Technically we could write multiple different variants of various pieces of code now depending on the setting value but that’s a great way to have twice as many bugs too, so not really worth it)
I wish I could diff the behavior of my game with and without this property enabled. My worst fear is this introducing bugs in my datastore layer (which is third party), or creating memory leaks. Too much of Roblox is not easily unit testable or possible to deeply analyze.
let us able to read SignalBehavior Property on workspace from code
like print(game:GetService(“Workspace”).SignalBehavior) ← SignalBehavior is not a valid member of Workspace “Workspace” (error)
After switching to Deferred, my game runs fine on studio, however, when I test on the live client, it freezes up and crashes.
The only point of interest in the client log dump was spamming this 0.572.0.5720484_20230421T140754Z_Player_8650F_last.log
2023-04-21T14:09:17.866Z,82.866776,144c,6 [FLog::Error] Exception caught in TGenericSlotWrapper. bad allocation
2023-04-21T14:09:17.866Z,82.866776,144c,6 [FLog::Error] Exception caught in TGenericSlotWrapper. bad allocation
2023-04-21T14:09:17.866Z,82.866776,144c,6 [FLog::Error] Exception caught in TGenericSlotWrapper. bad allocation
Unfortunately I don’t think this property will ever be script readable, but here’s a bit of a hacky workaround.
local isDeferred: boolean do
local function checkDeferred()
isDeferred = true
local bind = Instance.new("BindableEvent")
bind.Event:Once(function() isDeferred = false end)
bind:Fire()
bind:Destroy()
end
checkDeferred()
workspace.Changed:Connect(function( property )
if ( property ~= "SignalBehavior" ) then return end
checkDeferred()
end)
end
Oh, I moreso thought you needed it for a plugin, not in game.
What are you planning to make? If it’s something like an admin console, you can just leave in the first few lines and remove the .Changed part.
its for my networking module to track/detect what the game signalbehavior currently using on, so i can alert user to use :Once if it was Deferred, if possible my network library module could adapt with deferred without need change to :Once over :Listen (aka :Connect) without any problem happens
why not make a new method :Immediate() for the old behavior instead of a property of workspace that globally effects signal behavior in that place
also now since its live why cant we read workspace.SignalBehavior
nvm deferred events seem to be improved when they were in beta they had some issues but they work good now just tested them and they no longer “miss” being invoked like firing a bindable event in a for loop with no yielding
Correct. We intend to remove this property in the future so we will not make it script readable. If there are use-cases for needing to know the value of this property then we would prefer to resolve those another way.
Does this mean that “Deferred” will be the only option? Or did I interpret this wrong? Honestly, though this can boost performance by avoiding many event triggers like changing a property, which then triggers Change, and then in that Change event you do other changes, and like that becoming a chain.
But, I believe this behavior shouldn’t be a default in any way. When you make a change, add, or whatever it is, the event should automatically be called instead of waiting to the next frame like task.defer(if that’s what is meant by deferred) to do something, many things could have happened in that small time frame of 0.01 seconds.
Maybe what I used to do was just bad practice or what, because every time I change something I expect all the events listening to it to be called. Not sure if a function where you wrap Instance changes would be good Instance:Change(Property, Value) and then the engine does the rest.
Assuming this update mainly affects property changes related events.
Deferred just means “deferred until the next invocation point”, basically the next point where the engine isn’t in the middle of doing something. Once that invocation point is reached, event handlers are run until there are none left, including handlers invoked by other code run during the invocation point.
The “none left” in particular means no matter how many events you chain together your handler will never be delayed until the next frame or in any way “take longer” than before, your handlers just run in a different order than they did in immediate mode (after the invoking code rather than in the middle of it).
Enabling deferred mode on my experience has been causing Heartbeat events to run after being disconnected. Is this a bug?
Disconnecting a Heartbeat event at a certain time causes the event to run one extra frame after disconnection, and connection.Connected is always false at this time. This particularly causes issues with Roact, since a Heartbeat event runs one extra frame after being disconnected when a component unmounts. This doesn’t seem like intended behavior. You can reproduce it with this code:
local RunService = game:GetService("RunService")
local connection
connection = RunService.Heartbeat:Connect(function()
if not connection.Connected then
warn("disconnected but still runs")
end
end)
RunService.Heartbeat:Once(function()
connection:Disconnect()
end)
It outputs the warning 100% of the time with deferred events enabled, client or server. This problem does not happen unless the code that disconnects the event is run on Heartbeat, so I think it’s a bug? I can work around this, but I shouldn’t have to check if the event is still connected if it had just fired.
We are planning to change this in a future release so that all calls to disconnect are hard disconnects while things such as instance destruction remain soft disconnects.