Janitor is similar to Maid, you can use any of them, Janitor is a more maintained, better library with extra features, and better type checking, and overall to me it gives me a better developer experience.
Before anything, you can get Janitor by clicking here.
Janitor is a library that helps you keep track of things that need to be cleaned.
For example, a common use case, is storing RBXScriptConnections to then disconnect them at a later time, when they’re not needed anymore to prevent functions, instances from never being collected and extra threads possibly being created when not needed.
Why use Janitor?
This topic is not about trying to get you into Janitor, or even have any reference manager. However, if you’re looking for a reason to use Janitor instead of Maid, you can click here to see some of the reasons listed by @pobammer.
I would rather this topic does not turn into a discussion about it. If you wanna have conversation about it, go on #scripting-support and create a topic or find one.
Janitor is a class, so you can create multiple Janitor objects.
Commonly, you’ll have a Janitor for a player, sometimes for an instance, one for a specific script, tool, etc…
Creating a Janitor
You can create a Janitor object by calling Janitor.new
.
local janitor = Janitor.new()
We now have a janitor object, what can we do with it?
Adding objects to Janitor
Adding a RBXScriptConnection
You might wanna disconnect a ScriptConnection, you can do that by just calling :Add
on the Janitor object passing through the RBXScriptConnection.
janitor:Add(
BindableEvent.Event:Connect(function()
print("Fired")
end)
)
It’s important to note that ScriptConnections created from signals created by libraries like GoodSignal, FastSignal, and others fit into custom objects and not RBXScriptConnections. We will look into how we can do that soon.
Adding an Instance
Janitor has objects that it just automatically understands how to handle, those are RBXScriptConnections and functions. (functions explained later)
However, Instances are not part of that, that just means that you have to tell Janitor how to destroy it. This is simple, and it just consists of passing what the destroy method is called as a string as the second parameter.
janitor:Add(part, "Destroy")
Note: Janitor does auto handle calling :Destroy
when nothing is specified, however personally, I believe it is a best practice to always pass the method that needs to be called, as it gets into your muscle memory.
Adding a custom object
Like before, you just have to specify what method needs to be called, usually that will be :Destroy
or something else, in this case, in GoodSignal’s native implementation, it’s :DisconnectAll
.
janitor:Add(
GoodSignal.new(), "DisconnectAll"
)
Adding a function
Janitor also supports adding functions to the clean up list, what happens to these functions is that they’re just called when the Janitor is cleaned up.
janitor:Add(function()
print("This function is called when the object is cleaned up")
end)
Cleaning up a Janitor
Cleaning up a Janitor object means calling all the destroy methods on all the added objects and cleaning the Janitor’s references to these objects.
That are some ways of cleaning up a Janitor object, manually, automatically…
:Cleanup
Calling :Cleanup
does just that, no questions asked.
:Destroy
Calling :Destroy
on the Janitor object also calls :Cleanup
, and makes the janitor object unusable. Call it instead if you’re not gonna be using the object anymore.
Linking an Instance to Janitor
You might have to go through process of destroying cleaning up a Janitor object when an Instance is destroyed.
Thankfully, Janitor has a method that does that for you!
It’s called :LinkToInstance
.
janitor:Add(function()
print("This is called when the Janitor is destroyed")
end)
local part = Instance.new("Part", Workspace)
janitor:LinkToInstance(part)
part:Destroy()
-- Janitor should be destroyed now
Prematurely removing an object
Sometimes you’ll need destroy an object which was inside an object before the Janitor is cleaned up. In that case, you might want or even need to remove it from Janitor.
Removing an object from Janitor means also calling the specified destroy method, there’s no way around this as of writing this.
However, for you to be able to do that, you need to have specified an index for that object when calling :Add
. This index can be anything, and it’s the 3rd parameter of :Add
.
You can then, call :Remove
on the Janitor object with the index of the object.
janitor:Add(
Signal, "Destroy", "SomeEventThatHandlesSomething"
)
-- Some time later...
janitor:Remove("SomeEventThatHandlesSomething")
Using Janitor in Classes
A common use case for Janitor is using it to clean up anything that an object needs to stay alive, and then clean it up when it’s not needed anymore.
You’ll usually have a _janitor
member inside your class to do that.
function Timer.new(goal: number)
local self = setmetatable({
TimePassed = 0,
GoalReached = Signal.new(),
_goal = goal,
_janitor = Janitor.new()
}, Timer)
self._janitor:Add(self.GoalReached, "Destroy")
self._janitor:Add(
RunService.Heartbeat:Connect(function(deltaTime)
self.TimePassed += deltaTime
if self.TimePassed >= self._goal then
self.GoalReached:Fire()
self:Destroy()
end
end), "Disconnect"
)
return self
end
function Timer:Destroy()
local janitor = self._janitor
if janitor then
janitor:Destroy()
self._janitor = nil
end
end
This is where Janitor really shines, you should give it a try.
:Add
returns the object passed
This is a nice add that I couldn’t fit anywhere before on this topic, however it’s nice to know that unlike Maid, Janitor returns the object passed through :Add
which is helpful if you’re constructing the object right on the :Add
call.
local object = janitor:Add(
Object.new(), "Destroy", "Index"
)
object:method()
With all that, that’s it! That’s my starter guide to using Janitor, there’s still a lot more info you can get, but this should get you around with using Janitor on your project!
If you wanna look into API methods returns, other parameters, etc, you can look at Janitor’s documentation here.
Most notably, if you use Promise, Janitor has features to interact with it, and it’s worth to read the documentation for those.