Workspace.SignalBehavior 属性控制事件处理程序是立即触发还是延迟触发。建议使用 Enum.SignalBehavior.Deferred 选项,这有助于提高引擎的性能和正确性。延迟事件的事件处理程序将在下一个 恢复点 处恢复,并连同任何新触发的事件处理程序一起运行。
以下图表比较了 Immediate 事件行为和 Deferred 事件行为。
- 使用 Immediate 行为时,如果一个事件触发另一个事件,第二个事件处理程序会立即触发。
- 使用 Deferred 行为时,第二个事件会被添加到队列的末尾,稍后执行。
总耗时不变,但顺序不同。

“重入性”防止事件在达到一定深度时相互持续触发。当前的限制为 10。
延迟事件的好处
Immediate 行为存在一些缺点。对于添加到您的游戏中的每个实例、变更的属性或触发的其他事件,引擎需要在其他事情发生之前运行 Luau 代码。
- 要更改 1,000 个属性,可能需要在每次更改后运行 1,000 段代码。
- 可能会发生奇怪、难以诊断的错误,例如,在某些内容被添加之前移除事件就被触发。
- 性能关键系统可能会触发事件,导致它们来回让渡到 Luau。
- 事件处理程序可以在每次触发事件时对位置进行更改或触发其他事件。
- 尽管是多余的,事件仍可能触发多次,例如属性变化两次。
通过在引擎生命周期的特定阶段运行 Luau,引擎可以通过使用一些假设来获得更好的性能:
- 性能关键系统不需要让渡给 Luau,这会带来性能提升。
- 除非引擎本身发生更改,否则地点在恢复点之外不会更改。
恢复点
在被延迟后,事件处理程序会在下一个恢复点恢复。目前,恢复点的集合包括:
- 输入处理(每个要处理的输入恢复一次,参见 UserInputService)
- 旧版等待脚本恢复,如 wait()、spawn() 和 delay()
常见受影响的代码模式
使用远程事件时,以下示例可能无法正确工作或行为略有不同;它们依赖于事件立即恢复。
在执行中触发和捕获事件
在此示例中,启用延迟事件时始终返回 false,因为回调尚未执行。要正确工作,线程必须让渡,直到事件应该被触发的时刻。
local success = false
event:Connect(function ()
success = true
end)
doSomethingToTriggerEvent() -- 导致 `event` 被触发
return success
监听事件的第一次发生
connection = event:Connect(function ()
connection:Disconnect()
-- 做某事
end)
启用延迟事件时,多个事件处理程序调用可能在您从事件断开连接之前被排队。调用 Disconnect() 会丢弃所有待处理的事件处理程序调用——与立即事件的行为相同。
或者,使用 Once() 作为连接到您只需要首次调用的事件的更便捷方法。
改变祖先或属性的事件
延迟事件导致处理祖先或属性变化的事件在祖先或属性改变后触发:
local part = Instance.new("Part", workspace)
local function onPartDestroying()
print("在信号中:", part:GetFullName(), #part:GetChildren())
end
part.Destroying:Connect(onPartDestroying)
part:Destroy()
由于 Destroy() 在调用它的脚本让渡后立即生效,在调用 onPartDestroying() 时实例已被销毁。有关更多示例,请参阅 Instance.Destroying。