Deferred Engine Events: Rollout Update

yes, and not only performance also time writing code because now when the code is executed it’s guaranteed to be safely executed.

Specifically, this was an edge case when using lots of remote properties with Knit services.

Should be fixed by updating to the latest version of Knit

This update will heavily break my game, as it is causing code to error out in the most unexplainable ways. My events are now returning nil at random intervals, and the suggestions on how to fix the issues are not helping.

If anything, I would HEAVILY advise the team to see how these deferred systems respond in streaming enabled places, as actions done when a player spawns in locally are breaking.

For example, when a person spawns, it would collect what hats they have on, and make a list of them. Before deferred, everything worked amazingly. Now, everything breaks. I can’t see any logic on why it is breaking, but it is.

No one asked for this. No one was clamoring for this. And once “immediate” setting is unable to be used, my game will have to shut down.

1 Like

Toggled this on for our game.

Was a bit scared by replies here, but pretty much everything worked exactly as before. I’m not sure if I’m just ‘lucky’ to not be using breaking/unstable coding patterns or if the people above me are slightly exaggerating the issue.

I recommend you try it for your own experience and see if it works or not.

4 Likes

If you’re experiencing issues with deferred events then I would encourage you to submit a bug report or create a post in #help-and-feedback:scripting-support to ask for help. If you tag me or share it with me over message I will happily take a look to help you diagnose the issue.

2 Likes

Hi, this is a great update and it’s nice to see Roblox improving performance and security across the board. Kudos to you and your team for the good work. Although there is still a very large problem with this update.

Documentation.

In the first post of the Deferred Engine Events thread on April 7th, this line is stated:

We will also be updating our documentation on scheduling so keep an eye out for that in the coming weeks.

It has been 8 months, the update is rolling out, and just about none of the documentation on create.roblox.com/docs mention almost any of this new behavior and how it works. It has taken me searching the Developer Forum for multiple threads about deferred events and reading hundreds of comments on these threads to fully understand some of the potential footguns that I may run into from switching my game’s SignalBehavior from Immediate to Deferred.

Specifically three threads are where most of the information can be gleaned about deferred events:
November 2023: https://devforum.roblox.com/t/deferred-engine-events-rollout-update/2723113
April 2023: https://devforum.roblox.com/t/deferred-engine-events/2276564
May 2021: https://devforum.roblox.com/t/beta-deferred-lua-event-handling/1240569

Ideally developers should be able to go to the documentation to figure out the behavior of events and how they are deferred and how that affects the usage of BindableEvents, Changed connections, etc. They should not be having to go search across three different threads that exist over multiple years (making them slightly hard to even find) to understand how events work.

Almost none of the behavior or inner-workings of events mentioned in those three threads is mentioned in any Roblox Documentation page. It’s not mentioned in the BindableEvent page, the RBXScriptConnection page, the Custom Events and Callbacks page, or any page (that I can find at least).

Here is a great example of something that is not documented at all, which is Disconnect’s dropping all pending events associated with the connection:

Hard - Disconnect from the event immediately and drop all pending events associated with the connection.

I ran into a specific problem because of this recently when I connected to BindableEvent.Event, fired it, and then when a different module of mine disconnected that returned RBXScriptConnection on the same frame. I knew all three of these were on the same frame too, but I was still quite confused why the the single event fire wasn’t resolving, and went on a goose chase thinking my module had improper logic somehow. It may have been the case that pending events were always dropped, but previously at least this single fired event would’ve resolved so this dropping behavior wasn’t obvious. Perhaps this should be obvious behavior now due to the “deferred” terminology, but it really is one of those easy traps that new developers could easily fall into in which there’s not much documentation to help them.

So yeah. It would be nice if we got updated official documentation on all the intricacies of how events work on create.roblox.com/docs, as mentioned in the April 7th post.

5 Likes

How is the Destroying event useless? I have been writing deferred compatible code for over a year and I have never found the Destroying event to be “useless” in deferred mode, it works exactly as I would expect it to. When stuff is destroyed it fires at the end of the tick during the deferred stage in the engine, which is exactly what I would want it to do in deferred mode.

Deferred events just fire at the end of the frame. It isn’t as if the order in which events fire has become random or arbitrary or something, events still fire in the expected order, they just fire at a different time in the cycle, and they fire grouped up together.

The main benefit of event deferrals is that changes can (and already are) often grouped up into the deferred stage. It is like a final event point at the very end of the tick. In fact I have made intentional optimizations which take advantage of the engine’s ability to reduce work in deferred code. If you make changes to model bounding boxes for example in the deferred step e.g. using manual joint destruction or :BreakJoints() you will find that the engine does a lot less work, with the former being by far the fastest.

I am also not sure why you would ever need or want BindableEvents to fire immediately. With the way that I conceptualize events I just expect to throw the events out and have them be processed whenever, because that’s kinda what I think of when I hear event. I am not really sure what other case the order could or would actually be that important unless events aren’t being used like events or something.

I personally haven’t really ever ran into any issues unless I’m doing something weird. I’ve had a few minor issues at times because of cases where I fired events in events and expected them to make immediate changes to some data or variable or something because I was using them weirdly, but usually then I wasn’t using events as events anyways and replacing them with some different abstraction was always the solution.

1 Like

Are cleanup scripts ever going to be fixed, or will I have to use this?

task.spawn(function()
   script.Destroying:Wait()
		-- Cleanup
end)

With deferred events, I made a script that changed the CanCollide property of a part in a for loop. Then, I set up two connections to print CanCollide: a normal one, and a parallel one.

CanCollide code:

wait(1)
for i = 1, 50 do
	workspace.Part.CanCollide = not workspace.Part.CanCollide
	print("Set CanCollide to", workspace.Part.CanCollide)
end

Normal connection:

workspace.Part:GetPropertyChangedSignal("CanCollide"):Connect(function()
	print("Normal connection sees CanCollide as", workspace.Part.CanCollide)
end)

Parallel connection (script is parented to an Actor):

workspace.Part:GetPropertyChangedSignal("CanCollide"):ConnectParallel(function()
	print("Parallel connection sees", workspace.Part.CanCollide)
end)

I expected at least one of the connections to see the CanCollide property change. However,
image

1 Like

Can anybody please advise, perhaps @colbert2677 ?, how to solve this use case with deferred signaling enabled?
I want to know the position of a primary part of a model, when it is being removing/removed from workspace. Problem: The event handler will only kick in after all the children of the model are already parented to nil, and they are no longer accessible referencing the model. Basically, how can I replace the following code that used to work with immediate signaling? Thank you.

workspace.ChildRemoved:Connect(function(model)
	print(model.PrimaryPart.Position)
end)
1 Like

Try and use

local model = workspace.ChildRemoved:Wait()

print(model.PrimaryPart.Position)

I found a workaround. GetPivot() can still retrieve the model’s PrimaryPart position even after it is already parented to nil.

workspace.ChildRemoved:Connect(function(model)
	print(model:GetPivot().Position)
end)
1 Like

welp time to have a week of testing deferred, modified very few lines of code to make it work surprisingly.

Is there a plan to rework any of the Destroying or Removing events to better reflect deferred signaling?

The purpose of these events was to access or listen to something right before it got destroyed or removed, but since any functions connected to these events are going to be executed at a deferred point in the current frame, when they finally get executed, the object will already have been destroyed or removed.
That would be useful for a .Removed or .Destroyed event, but not for a .Removing or .Destroying event whose purpose was to allow us to execute something before it gets destroyed/removed.

3 Likes

Blockquote You should keep in mind that the majority of these affect <10%, sometimes even <1% of experiences in the first place

Where did you get this percentage from?
Also 10% of experiences is a lot since if theres around 5.5 Million experiences that means it effects around 550,000 experiences. That’s quite a lot. I agree with @ChipioIndustries eventually I’m gonna move on to something else, but It’s gonna be likely that my games are gonna break within a few years because of updates like these.

ps. I am not against this update, in fact It’s quite the opposite, I support it. I just have some concerns such as it being forced… unless I’m getting the wrong idea, if I am please correct me.

I think the main problem is that destroying is supposed to run BEFORE the part is destroyed
image

4 Likes

I don’t know if this is a bug or whether I did something wrong but after testing something the Instance.Destroying signal actually didn’t fire at all on a client-sided script.

I had to use the .Changed signal instead when an object is parented to nil and it felt weird to do it that way.
It kinda is how we used to write clean-up scripts but weird that .Destroying doesn’t fire when a object is destroyed.

1 Like

Hey, everyone, I’ve been trying to convert my game to using deferred engine events since it is becoming the new standard, but I have had some issues. Specifically, issues with setting variables in scripts, or at least seems to be a recurring issue. Let me show you an example:
image
image


As you can see, the first variable seems to set up, but on the second one, it occasionally throws errors. Both versions of the variable create this error. This is the very first thing listed in the script. It is not a local script, and the tool cannot be dropped or unequipped. This issue has only been happening with deferred events turned on, and there is no issue with immediate as the setting. Maybe I’ve just been setting up variables incorrectly all this time. Any help or suggestions to fix this would be appreciated. Thanks

@WallsAreForClimbing @tnavarts

Have the events that are connected to instance destroying been fixed? If I recall this update broke that, or so I’ve been told.

Also, even if this becomes a new standard, are we still going to have it as an optional flag? I’m much comfortable with the immediate as I’m not aware of any actual improvements that deferred does besides affecting how events are scheduled.

2 Likes

Destroying works, but if the Deferred event gets rolled out, the naming does not make sense anymore.

.Destroying → .Destroyed
.CharacterRemoving → .CharacterRemoved
.PlayerRemoving → .PlayerRemoved

@WallsAreForClimbing @tnavarts What would happen to these names? It’s now misleading in that the part is already destroyed but it’s being indicated as a present tense event.