I would say I was pretty close to finishing the logic of all of that, but I have come to the strange wall that makes me think that unfortunately there is a bug / bugs here. But I don’t know, maybe someone can fully finish my logic.
My assumption #1: No, PostSimulation and Heartbeat are different things and I will explain why you can think that they are in fact the same thing, but I want to say that graph from docs is correct but there are still some missing parts.
My assumption #2: Yes, you are right. Heartbeat and task.wait() have some differencies.
Here is again the code that we will use:
Also we will again use the graph from the site:
--!strict
local RunService = game:GetService("RunService")
RunService.PreRender:Connect(function()
print("PreRender")
task.defer(function()
print("Deferred PreRender")
end)
end)
RunService.PreAnimation:Connect(function()
print("PreAnimation")
task.defer(function()
print("Deferred PreAnimation")
end)
end)
RunService.PreSimulation:Connect(function()
print("PreSimulation")
task.defer(function()
print("Deferred PreSimulation")
end)
end)
RunService.PostSimulation:Connect(function()
print("PostSimulation")
task.defer(function()
print("Deferred PostSimulation")
end)
end)
RunService.Heartbeat:Connect(function()
print("Heartbeat")
task.defer(function()
print("Deferred Heartbeat")
end)
end)
while true do
task.wait()
print("Loop!")
task.defer(function()
print("Deferred loop!")
end)
end
Let’s test in Immediate mode.
14:48:28.990 PreRender - Client - LocalScript:7
14:48:28.992 Deferred PreRender - Client - LocalScript:9
14:48:29.072 PreAnimation - Client - LocalScript:15
14:48:29.072 Deferred PreAnimation - Client - LocalScript:17
14:48:29.072 PreSimulation - Client - LocalScript:23
14:48:29.072 Deferred PreSimulation - Client - LocalScript:25
14:48:29.073 PostSimulation - Client - LocalScript:31
14:48:29.073 Loop! - Client - LocalScript:50
14:48:29.090 Heartbeat - Client - LocalScript:39
14:48:29.090 Deferred PostSimulation - Client - LocalScript:33
14:48:29.090 Deferred loop! - Client - LocalScript:52
14:48:29.090 Deferred Heartbeat - Client - LocalScript:41
Let’s now don’t focus on Deferred loop!, Deferred PostSimulation, Deferred Heartbeat.
As we can see, as documentation said, we have PostSimulation → Loop (“Queue Delayed Scripts”) → Heartbeat. Everything seems to work fine.
Let’s switch to Deferred mode:
14:52:02.357 PreRender - Client - LocalScript:7
14:52:02.357 Deferred PreRender - Client - LocalScript:9
14:52:02.717 PreAnimation - Client - LocalScript:15
14:52:02.717 Deferred PreAnimation - Client - LocalScript:17
14:52:02.717 PreSimulation - Client - LocalScript:23
14:52:02.718 Deferred PreSimulation - Client - LocalScript:25
14:52:02.718 Loop! - Client - LocalScript:50
14:52:02.752 PostSimulation - Client - LocalScript:31
14:52:02.752 Deferred loop! - Client - LocalScript:52
14:52:02.753 Heartbeat - Client - LocalScript:39
14:52:02.753 Deferred PostSimulation - Client - LocalScript:33
14:52:02.753 Deferred Heartbeat - Client - LocalScript:41
Now you can think: hm, PostSimulation and Heartbeat are the same things.
But what happens in fact? Let’s not forget that handlers in Deferred mode are executed on invocation points. Here is a list of this points that Roblox have provided here: [Beta] Deferred Lua Event Handling.
RenderStepped
Waiting script resumption (e.g. wait, spawn, delay)
Stepped
Heartbeat
BindToClose
Here where this realisation comes: yes, PostSimulation happens before “Queue Delayed Scripts”, as we saw in Immediate mode. And even now it happened before. But PostSimulation is not invocation point. That’s why connected function to PostSimulation was called only after “Loop!” was printed. It’s because while true loop was resumed through task.wait() so it is Waiting script resumption invocation point, so our queue of callbacks had inside of it function that should print PostSimulation. After that we, as graph suggested, go to Heartbeat.
But the problem is that I am not able to understand the order of task.defer() unfortunately since there is no description of term “end of resume point” which is used in docs in description of task.defer(). It is reasonable to say that resume points are “Input Events” are fired, callbacks are executed; “PreRender Event” is fired, callbacks are executed and so on but unfortunately this logic doesn’t work when it comes to “Queue Delayed Scripts”, Heartbeat and PostSimulation so I don’t know.
