Janitor
Janitor is a light-weight, flexible object for cleaning up connections, instances, or anything. This implementation covers all use cases, as it doesn't force you to rely on naive typechecking to guess how an instance should be cleaned up. Instead, the developer may specify any behavior for any object.
This is the fastest OOP library on Roblox of its kind on both X86-64 as well as ARM64.
Properties
CurrentlyCleaning
This item is read only and cannot be modified. Read OnlyJanitor.CurrentlyCleaning:
boolean
Whether or not the Janitor is currently cleaning up.
SuppressInstanceReDestroy
Janitor.SuppressInstanceReDestroy:
boolean
Whether or not you want to suppress the re-destroying of instances. Default is false, which is the original behavior.
UnsafeThreadCleanup
Janitor.UnsafeThreadCleanup:
boolean
Whether or not to use the unsafe fast defer function for cleaning up threads. This might be able to throw, so be careful. If you're getting any thread related errors, chances are it is this.
Functions
new
Instantiates a new Janitor object.
Is
Janitor.
Is
(
object:
unknown
--
The object you are checking.
) →
boolean
--
true
if object
is a Janitor.
Determines if the passed object is a Janitor. This checks the metatable directly.
instanceof
Janitor.
instanceof
(
object:
unknown
--
The object you are checking.
) →
boolean
--
true
if object
is a Janitor.
An alias for Janitor.Is. This is intended for roblox-ts support.
Add
Janitor:
Add
(
object:
T
,
--
The object you want to clean up.
methodName?:
boolean
|
string
,
--
The name of the method that will be used to clean up. If not passed, it will first check if the object's type exists in TypeDefaults, and if that doesn't exist, it assumes Destroy
.
index?:
unknown
--
The index that can be used to clean up the object manually.
) →
T
--
The object that was passed as the first argument.
Adds an object
to Janitor for later cleanup, where methodName
is the
key of the method within object
which should be called at cleanup time.
If the methodName
is true
the object
itself will be called if it's a
function or have task.cancel
called on it if it is a thread. If passed an
index it will occupy a namespace which can be Remove()
d or overwritten.
Returns the object
.
Note
Objects not given an explicit methodName
will be passed into the typeof
function for a very naive typecheck. RBXConnections will be assigned to
"Disconnect", functions and threads will be assigned to true
, and
everything else will default to "Destroy". Not recommended, but hey, you do
you.
Luau:
local Workspace = game:GetService("Workspace")
local TweenService = game:GetService("TweenService")
local obliterator = Janitor.new()
local part = Workspace:FindFirstChild("Part") :: Part
-- Queue the Part to be Destroyed at Cleanup time
obliterator:Add(part, "Destroy")
-- Queue function to be called with `true` methodName
obliterator:Add(print, true)
-- Close a thread.
obliterator:Add(task.defer(function()
while true do
print("Running!")
task.wait(0.5)
end
end), true)
-- This implementation allows you to specify behavior for any object
obliterator:Add(TweenService:Create(part, TweenInfo.new(1), {Size = Vector3.one}), "Cancel")
-- By passing an index, the object will occupy a namespace
-- If "CurrentTween" already exists, it will call :Remove("CurrentTween") before writing
obliterator:Add(TweenService:Create(part, TweenInfo.new(1), {Size = Vector3.one}), "Destroy", "CurrentTween")
TypeScript:
import { Workspace, TweenService } from "@rbxts/services";
import { Janitor } from "@rbxts/janitor";
const obliterator = new Janitor<{ CurrentTween: Tween }>();
const part = Workspace.FindFirstChild("Part") as Part;
// Queue the part to be Destroyed at Cleanup time
obliterator.Add(part, "Destroy");
// Queue function to be called with `true` methodName
obliterator.Add(print, true);
// Close a thread.
obliterator.Add(task.defer(() => {
while (true) {
print("Running!");
task.wait(0.5);
}
}), true);
// This implementation allows you to specify behavior for any object
obliterator.Add(TweenService.Create(part, new TweenInfo(1), { Size: Vector3.one }), "Cancel");
// By passing an index, the object will occupy a namespace
// If "CurrentTween" already exists, it will call :Remove("CurrentTween") before writing
obliterator.Add(TweenService.Create(part, new TweenInfo(1), { Size: Vector3.one }), "Destroy", "CurrentTween");
AddObject
Janitor:
AddObject
(
constructor:
{
new:
(
A...
)
→
T
}
,
--
The constructor for the object you want to add to the Janitor.
methodName?:
boolean
|
string
,
--
The name of the method that will be used to clean up. If not passed, it will first check if the object's type exists in TypeDefaults, and if that doesn't exist, it assumes Destroy
.
index?:
unknown
,
--
The index that can be used to clean up the object manually.
...:
A...
--
The arguments that will be passed to the constructor.
) →
T
--
The object that was passed as the first argument.
Constructs an object for you and adds it to the Janitor. It's really just
shorthand for Janitor:Add(object.new(), methodName, index)
.
Luau:
local obliterator = Janitor.new()
local subObliterator = obliterator:AddObject(Janitor, "Destroy")
-- subObliterator is another Janitor!
TypeScript:
import { Janitor } from "@rbxts/janitor";
const obliterator = new Janitor();
const subObliterator = obliterator.AddObject(Janitor, "Destroy");
Get
Janitor:
Get
(
index:
unknown
--
The index that the object is stored under.
) →
unknown?
--
This will return the object if it is found, but it won't return anything if it doesn't exist.
Gets whatever object is stored with the given index, if it exists. This was
added since Maid allows getting the task using __index
.
Luau:
local obliterator = Janitor.new()
obliterator:Add(Workspace.Baseplate, "Destroy", "Baseplate")
print(obliterator:Get("Baseplate")) -- Returns Baseplate.
TypeScript:
import { Workspace } from "@rbxts/services";
import { Janitor } from "@rbxts/janitor";
const obliterator = new Janitor<{ Baseplate: Part }>();
obliterator.Add(Workspace.FindFirstChild("Baseplate") as Part, "Destroy", "Baseplate");
print(obliterator.Get("Baseplate")); // Returns Baseplate.
AddPromise
Janitor:
AddPromise
(
promiseObject:
Promise
,
--
The promise you want to add to the Janitor.
index?:
unknown
--
The index that can be used to clean up the object manually.
) →
Promise
Adds a Promise to the Janitor. If the Janitor is cleaned up and the Promise is not completed, the Promise will be cancelled.
Luau:
local obliterator = Janitor.new()
obliterator:AddPromise(Promise.delay(3)):andThenCall(print, "Finished!"):catch(warn)
task.wait(1)
obliterator:Cleanup()
TypeScript:
import { Janitor } from "@rbxts/janitor";
const obliterator = new Janitor();
obliterator.AddPromise(Promise.delay(3)).andThenCall(print, "Finished!").catch(warn);
task.wait(1);
obliterator.Cleanup();
Errors
Type | Description |
---|---|
NotAPromiseError | Thrown if the promise is not a Promise. |
Remove
Cleans up whatever Object
was set to this namespace by the 3rd parameter of Janitor.Add.
Luau:
local obliterator = Janitor.new()
obliterator:Add(workspace.Baseplate, "Destroy", "Baseplate")
obliterator:Remove("Baseplate")
TypeScript:
import { Workspace } from "@rbxts/services";
import { Janitor } from "@rbxts/janitor";
const obliterator = new Janitor<{ Baseplate: Part }>();
obliterator.Add(Workspace.FindFirstChild("Baseplate") as Part, "Destroy", "Baseplate");
obliterator.Remove("Baseplate");
RemoveNoClean
Removes an object from the Janitor without running a cleanup.
Luau
local obliterator = Janitor.new()
obliterator:Add(function()
print("Removed!")
end, true, "Function")
obliterator:RemoveNoClean("Function") -- Does not print.
TypeScript:
import { Janitor } from "@rbxts/janitor";
const obliterator = new Janitor<{ Function: () => void }>();
obliterator.Add(() => print("Removed!"), true, "Function");
obliterator.RemoveNoClean("Function"); // Does not print.
RemoveList
Cleans up multiple objects at once.
Luau:
local obliterator = Janitor.new()
obliterator:Add(function()
print("Removed One")
end, true, "One")
obliterator:Add(function()
print("Removed Two")
end, true, "Two")
obliterator:Add(function()
print("Removed Three")
end, true, "Three")
obliterator:RemoveList("One", "Two", "Three") -- Prints "Removed One", "Removed Two", and "Removed Three"
TypeScript:
import { Janitor } from "@rbxts/janitor";
type NoOp = () => void
const obliterator = new Janitor<{ One: NoOp, Two: NoOp, Three: NoOp }>();
obliterator.Add(() => print("Removed One"), true, "One");
obliterator.Add(() => print("Removed Two"), true, "Two");
obliterator.Add(() => print("Removed Three"), true, "Three");
obliterator.RemoveList("One", "Two", "Three"); // Prints "Removed One", "Removed Two", and "Removed Three"
RemoveListNoClean
Cleans up multiple objects at once without running their cleanup.
Luau:
local obliterator = Janitor.new()
obliterator:Add(function()
print("Removed One")
end, true, "One")
obliterator:Add(function()
print("Removed Two")
end, true, "Two")
obliterator:Add(function()
print("Removed Three")
end, true, "Three")
obliterator:RemoveListNoClean("One", "Two", "Three") -- Nothing is printed.
TypeScript:
import { Janitor } from "@rbxts/janitor";
type NoOperation = () => void
const obliterator = new Janitor<{ One: NoOperation, Two: NoOperation, Three: NoOperation }>();
obliterator.Add(() => print("Removed One"), true, "One");
obliterator.Add(() => print("Removed Two"), true, "Two");
obliterator.Add(() => print("Removed Three"), true, "Three");
obliterator.RemoveListNoClean("One", "Two", "Three"); // Nothing is printed.
GetAll
Janitor:
GetAll
(
) →
{
[
any
]
:
any
}
Returns a frozen copy of the Janitor's indices.
Luau:
local obliterator = Janitor.new()
obliterator:Add(Workspace.Baseplate, "Destroy", "Baseplate")
print(obliterator:GetAll().Baseplate) -- Prints Baseplate.
TypeScript:
import { Workspace } from "@rbxts/services";
import { Janitor } from "@rbxts/janitor";
const obliterator = new Janitor<{ Baseplate: Part }>();
obliterator.Add(Workspace.FindFirstChild("Baseplate") as Part, "Destroy", "Baseplate");
print(obliterator.GetAll().Baseplate); // Prints Baseplate.
Cleanup
Janitor:
Cleanup
(
) →
(
)
Calls each object's methodName
(or calls the object if
methodName == true
) and removes them from the Janitor. Also clears the
namespace. This function is also called when you call a Janitor object (so
it can be used as a destructor callback).
Luau:
obliterator:Cleanup() -- Valid.
obliterator() -- Also valid.
TypeScript:
obliterator.Cleanup()
// TypeScript version doesn't support the __call method of cleaning.
Destroy
Janitor:
Destroy
(
) →
(
)
Calls Janitor.Cleanup and renders the Janitor unusable.
Metatable Removal
Running this will make any further attempts to call a method of Janitor error.
LinkToInstance
Janitor:
LinkToInstance
(
allowMultiple?:
boolean
--
Whether or not to allow multiple links on the same Janitor.
) →
RBXScriptConnection
--
A RBXScriptConnection that can be disconnected to prevent the cleanup of LinkToInstance.
"Links" this Janitor to an Instance, such that the Janitor will Cleanup
when the Instance is Destroy()
d and garbage collected. A Janitor may only
be linked to one instance at a time, unless allowMultiple
is true. When
called with a truthy allowMultiple
parameter, the Janitor will "link" the
Instance without overwriting any previous links, and will also not be
overwritable. When called with a falsy allowMultiple
parameter, the
Janitor will overwrite the previous link which was also called with a falsy
allowMultiple
parameter, if applicable.
Luau:
local obliterator = Janitor.new()
obliterator:Add(function()
print("Cleaning up!")
end, true)
do
local folder = Instance.new("Folder")
obliterator:LinkToInstance(folder)
folder:Destroy()
end
TypeScript:
import { Janitor } from "@rbxts/janitor";
const obliterator = new Janitor();
obliterator.Add(() => print("Cleaning up!"), true);
{
const folder = new Instance("Folder");
obliterator.LinkToInstance(folder, false);
folder.Destroy();
}
LinkToInstances
Janitor:
LinkToInstances
(
) →
Janitor
--
A new Janitor that can be used to manually disconnect all LinkToInstances.
Links several instances to a new Janitor, which is then returned.