[Beta] Deferred Lua Event Handling

Does this mean there will be a separate queue for each of these?


Personally this one will be the biggest issue. I never used funny tricks like FastSpawn or inline resumption (which seems like a bad practice in the first place).

A big reason for me not setting SignalBehavior to Deferred is because a lot of popular plugins (e.g F3X Building Tool) rely on it being Immediate, so we cant just enable it even tho the game can run fine with the Deferred.

Perhaps something like workspace.PluginsSignalBehavior to separate the plugins from the game would make the push for Deferred a little easier.

Any update on this?

I am planning to make a new project soon. If this update was released in it’s current state, it would flip some of my coding patterns upside down. I don’t want to spend time writing code just for it to be obsolete shortly.

Hi, we’re currently waiting for Deferred signal behavior to be live before we can release a fully-fledged project properly (which is going to arrive closer to release soon). We rely on the signal behavior as it has fixed numerous weird edge cases and we have adapted the codebase to it too – testing out the project live with Immediate signal behavior breaks most of our code!

Can you guys please try to have this new signal behavior live ASAP, or at least, provide some way for us to toggle it in live servers?

1 Like

It’s been quite some time but I wanted to give you all an update on this. Given the number of concerns that were raised initially we wanted to take some time to make sure that a number of features were in place to make transitioning as smooth as possible, or offer reasonable alternatives. A number of teams are actively working on this effort but I cannot give you an ETA at the moment, just know it is on our radar.

If you have any specific, time-sensitive, concerns then feel free to reach out to me directly via my messages.

Thanks!

5 Likes

What are your coding patterns that are causing problems with Deferred events?

I’m kind of lost on why we can’t enable it live? Like we can subject ourselves to potentially uncompatible code on Studio like we wouldn’t wanna disable it over on Studio too? I’m all in for Deferred event behaviour but letting me just test it out on Studio but not live is just annoying. It’s one of the reasons my FastSignal library has an auto-detect mode for what the game is using, because of this difference in live and non-live games (ofc also for ease of use and swapability with older signal libraries based on bindableevents but like really?)

There was / there’s much more breaking properties on Workspace that work on live games, why isn’t that the case for deferred events behaviour?

I got an IPv6 shirt, will I have to get an deferred event shirt??

1 Like

You can always change the ways you go about what’s currently breaking your code. ConnectOnce is a matter of just checking if it was disconnected or not. Also I’m relatively sure that Roblox added a :Once function a while ago, though I’m not sure if it’s actually enabled. As for if you’re using a certain signal library that’s breaking with Deferred behaviour, FastSignal (mine the right one) has full support.

Predictable code usually doesn’t break because of Deferred events and when it does, it’s minor and should be easy to change. It’s not that hard to migrate to Deferred event behaviour because whatever works on Deferred mostly works on Immediate, just not the other way around.

1 Like

Well, actually, none that can’t be fixed in a few minutes.

That still doesn’t fully replace Immediate’s current behaviour, e.g. if you have a connection that will disconnect itself if a certain condition is met. Yes, you can make it work, but it will unnecessarily run the connection more times than it should be ran and it triggers my OCD honestly (putting performance impact aside as I bet that it’s negligible).

Iv run into a problem that does not feel correct

I have 2 scripts

image

-- ScriptA
local scriptB = game.ServerStorage.ScriptB
local event = game.ServerStorage.ScriptB.BindableEvent
scriptB.Parent = game.ServerScriptService
event:Fire()
-- ScriptB
script.BindableEvent.Event:Connect(function()
	print("Event")
end)

if SignalBehavior is set to Default then Event gets printed to output no problem

but if SignalBehavior is set to Deferred then Event never get printed

if you want to try it for your self here is a project
Deferred.rbxl (37.3 KB)

2 Likes

I have a suggestion

the problem is that this property is global meaning that it effects all events in the project

this can be a problem when creating public modules/libraries that will be used by other developers where you don’t have control over the SignalBehavior property and if you have time sensitive code this property can break your module

so my suggestion is to add a new SignalBehavior property to bindable events and functions

local bindableEvent = Instance.new("BindableEvent")

-- my suggestion
bindableEvent.SignalBehavior = Enum.SignalBehavior.Default

bindableEvent.Event:Connect(function()
	print("Fired")
end)

bindableEvent:Fire()

so this will allow us to bypass the SignalBehavior property for individual events

Mixing SignalBehaviour usually creates a lot of issues, as code that would otherwise run immediately is now running after some other code and it becomes way harder to migrate games to Deferred. If your game is using a custom signal library, switch to something like FastSignal that supports and adapts to whatever mode your game is using.

If your code works on Deferred, it will usually work on Immediate no issue. (except for some instances like when you build your own signal library but that’s not really what I’m talking about)

This is because the Fire is running before the connection, so when the fire happens, there is no connection therefore it doesn’t have anything to fire, this is a race condition, some internal code is definitely changing the order on which what script runs first, and I am aware that events that would fire on the server start end up running before any developer-made script is ran on Deferred, this is weird but my suggestion on any case is to deal with this race condition by having code that does with it, so with a check, for example, some people had issues where .PlayerAdded would connect only after the player joined at least on Studio, and so that player never had code handling their entrance, however the best practice is actually to deal with the current situation, when you :Connect you’re talking about when something in the future happens, so if you want it to happen to everything, you should always have code that also handles with the current situation, so in this case for example, you would do :GetPlayers and run the function on the found players so that you don’t have a risk of a potential race condition.

1 Like

My module I created requires signals to spawn and not defer I had no choice but to create my own signal module so that whoever is using my module can freely change the SignalBehavior property and not break the module this works but ideally I would prefer to use Roblox’s blindableevents but at the current time is not a option


yer its a strange race condition, I guess internally the script is loading after a defer causing the fire to get called first but it feels correct that when I set the scripts parent the script should load instantly and should not be deferred

I would rather have a new method added called “ConnectDeferred” so we can use either method anytime we want making it flexible and fixing both problems with introducing deferred connections.


local BindableEvent=Instance.new("BindableEvent")

--Immediate

BindableEvent.Event:Connect(function(...)

print("Immediate ",...)

end)

--Deferred

BindableEvent.Event:ConnectDeferred(function(...)

print("Deferred ",...)

end)

5 Likes

This would not fix my problem

the script that fires the event would need to be able to control if its spawned or deferred

so we would need functions like FireSpawn & FireDeferred

1 Like

BindableEvent:Fire() → check for deferred connections add to deferred que → check for immediate connections add to immediate que → handler

2 Likes

How are we expected to create self cleaning scripts using this event behavior? say I wanted to run some code to clean up any changes the script has done once it’s destroyed, any event that I tried like .Destroyed or .AncestryChanged never actually fired the callback since the script thread was terminated before the deferred event was processed

1 Like

This is a significant change! While I see the benefits of it, I also see some potential issues. I’m not sure if I would want to use Deferred for every Event or RenderSteeped, but for certain cases, it could be very useful. Instead of having an option to enable or disable it globally, it would be interesting to have the ability to choose the SignalBehavior on a per-event basis. For example, it would be great to be able to use something like this:

Event.SignalBehavior = Enum.SignalBehavior.Deferred
Event:FireTo(Player)

Overall, I think this new feature has a lot of potential, but it’s crucial to have the flexibility to define the SignalBehavior for each event specifically. Otherwise, it could become more of an annoyance than a useful addition.

1 Like

The point of this is for you to learn how to adapt to the deferred behaviour and apply it everywhere, since if you can half commit to it why not fully? Its supposed to be the least buggiest way of doing things from what I’ve read.

I tried enabling deferred event in my game, and as expected, some things broke.
However, I do see that even core scripts are getting errors. For example in the GameTranslator module in CoreScripts had an error with a connection variable set to nil. It looks like it happens when tools are destroyed when the character respawns.

2 Likes

When the signal behavior is set to Deferred, it stops the plugin.Unloading event from firing when a plugin is disabled, updated, or a place file is closing.

This prevents last-minute operations like cleaning up extra objects created by the plugin or saving plugin data.


To see this effect in action,

  1. Make sure the SignalBehavior property of image is set to Default or Immediate and that the Output widget is open (image tab > image)

  2. Create a new Script with the following code inside it,

plugin.Unloading:Connect(function()
	print("I'm unloading!")
end)
  1. Right-click that script and choose image in the dropdown. For convenience, name the file the name of the script, we’ll need to overwrite this file later.

  2. Repeat step 3 and check the output, it should display image.

  3. Change the SignalBehavior property of image to Deferred and repeat step 3.

  4. Nothing should print in the output now.

You can delete the plugin file by navigating to the image tab, clicking image, and re-opening the place file you’re in.

1 Like