Skip to content

Instantly share code, notes, and snippets.

@lucasmz-dev
Last active November 16, 2021 02:53
Show Gist options
  • Save lucasmz-dev/c8f08dbd1a84c60513cb991b2836c99a to your computer and use it in GitHub Desktop.
Save lucasmz-dev/c8f08dbd1a84c60513cb991b2836c99a to your computer and use it in GitHub Desktop.
Proper BindToClose implementation
--[[
A custom BindToClose implementation which fixes these issues:
* Has a :Disconnect method
* Listeners run in a different order:
Which is: First connected -> Last connected
This order fixes a lot of hidden issues, particularly with requiring.
]]
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ScriptSignal = require(ReplicatedStorage.Utils.Signal.Immediate)
-- Must be an immediate mode signal! I am using FastSignal Immediate, you can use GoodSignal.
local BindToClose = ScriptSignal.new()
local HandlerClosed = ScriptSignal.new()
local ActiveHandlers = 0
game:BindToClose(function()
BindToClose:Fire()
while ActiveHandlers > 0 do
HandlerClosed:Wait()
end
end)
return function(
handler: () -> ()
): ScriptSignal.ScriptConnection
return BindToClose:Connect(function()
ActiveHandlers += 1
local connection
local thread
local hasThreadDied = false
task.spawn(function()
thread = coroutine.running()
handler()
hasThreadDied = true
ActiveHandlers -= 1
HandlerClosed:Fire()
end)
connection = RunService.Heartbeat:Connect(function()
if hasThreadDied then
connection:Disconnect()
return
end
if coroutine.status(thread) == 'dead' and hasThreadDied == false then
-- This is used to detect when a thread errors
connection:Disconnect()
ActiveHandlers -= 1
HandlerClosed:Fire()
return
end
end)
end)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment