Janitor

repository·main·Indexed 40 days ago

https://github.com/howmanysmall/janitor

A high-performance, thread-safe garbage collection management library for Roblox. It provides a unified interface to track and clean up tasks, connections, and instances to prevent memory leaks. Key features include custom cleanup methods, instance linking via LinkToInstance, native Promise support through AddPromise, and the ability to manage objects using custom reference keys.

Tokens
2.4K
Snippets
4
Records
9
Agent score
98%

What's inside Janitor

  1. What is Janitor and why use it?

    main

    Janitor is a library designed to simplify garbage collection by managing cleanup tasks through a unified interface. It is a thread-safe version that avoids using global state.

    Key advantages over similar libraries like Maid include:

    • Flexible Add method: Janitor:Add returns the object that was added, allowing for immediate use.
    • Custom cleanup methods: You can pass a custom method to Janitor:Add to specify how an object should be cleaned up (e.g., calling :Stop() on a Tween instead of a standard destroy method).
    • Custom reference keys: You can provide a custom string key to store the object under within the Janitor, maintaining API consistency.
    • Instance linking: You can link a Janitor to a Roblox Instance. When that Instance is destroyed, the Janitor automatically triggers its cleanup process for all managed tasks.
  2. Janitor features: Promises and TypeScript

    main

    Janitor includes specialized support for modern development workflows:

    • Native Promise Support: Janitor supports cancelling Promises during the cleanup process via Janitor:AddPromise().
    • TypeScript Support: The library provides full IntelliSense for stored objects and available cleanup methods, making it highly compatible with TypeScript environments.
  3. Use custom cleanup methods with Janitor:Add

    main

    Unlike other libraries that only support specific methods like :Destroy() or :Disconnect(), Janitor allows you to specify any method to be called during cleanup. This is useful for stopping tweens, deactivating tools, or breaking joints. You can pass the method name as a string as the second argument to Janitor:Add().

    -- Example: Calling a specific method instead of :Destroy()
    Janitor:Add(tween, "Cancel")
    Janitor:Add(humanoid, "BreakJoints")
  4. Install Janitor via HttpService

    main

    You can install Janitor in Roblox Studio using a script executed in the Command Bar. This method uses HttpService to fetch the source files from GitHub and reconstruct the module structure in ReplicatedStorage.

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local HttpService = game:GetService("HttpService")
    local HttpEnabled = HttpService.HttpEnabled
    HttpService.HttpEnabled = true
    local function RequestAsync(RequestDictionary)
        return HttpService:RequestAsync(RequestDictionary)
    end
    local function GetAsync(Url, Headers)
        Headers["cache-control"] = "no-cache"
        local Success, ResponseDictionary = pcall(RequestAsync, {
            Headers = Headers;
            Method = "GET";
            Url = Url;
        })
        if Success then
            if ResponseDictionary.Success then
                return ResponseDictionary.Body
            else
                return false, string.format("HTTP %*: %*", ResponseDictionary.StatusCode, ResponseDictionary.StatusMessage)
            end
        else
            return false, ResponseDictionary
        end
    end
    local function Initify(Root)
        local InitFile = Root:FindFirstChild("init")
            or Root:FindFirstChild("init.luau") or Root:FindFirstChild("init.client.luau") or Root:FindFirstChild("init.server.luau")
            or Root:FindFirstChild("init.luau") or Root:FindFirstChild("init.client.luau") or Root:FindFirstChild("init.server.lua")
        if InitFile then
            InitFile.Name = Root.Name
            InitFile.Parent = Root.Parent
            for _, Child in Root:GetChildren() do
                Child.Parent = InitFile
            end
            Root:Destroy()
            Root = InitFile
        end
        for _, Child in Root:GetChildren() do
            Initify(Child)
        end
        return Root
    end
    local FilesList = HttpService:JSONDecode(assert(GetAsync(
        "https://api.github.com/repos/howmanysmall/Janitor/contents/src",
        {accept = "application/vnd.github.v3+json"}
    )))
    local Janitor = Instance.new("Folder")
    Janitor.Name = "Janitor"
    for _, FileData in FilesList do
        local ModuleScript = Instance.new("ModuleScript")
        ModuleScript.Name = tostring(string.match(FileData.name, "(%w+)%.luau?"))
        local Success, Source = GetAsync(FileData.download_url, {})
        if not Success then
            ModuleScript.Source = string.format("-- %*", tostring(Source))
        else
            ModuleScript.Source = tostring(Success)
        end
        ModuleScript.Parent = Janitor
    end
    Janitor.Parent = ReplicatedStorage
    Initify(Janitor)
    HttpService.HttpEnabled = HttpEnabled
  5. Link a Janitor to an Instance lifecycle with LinkToInstance

    main
    You can use Janitor:LinkToInstance(instance) to bind the Janitor's lifecycle to a specific Roblox Instance. When that Instance is destroyed via Instance:Destroy(), the Janitor will automatically trigger the cleanup of everything it is managing.
  6. Compare Janitor API capabilities with Maid and Trove

    main

    Janitor provides a more flexible API for managing instances and cleanup compared to Maid or Trove. Key advantages include:

    • Adding with Index: Janitor:Add(Instance, false, "Index") allows you to assign a custom reference/index.
    • Custom Cleanup: Janitor:Add(Object, "MethodName") allows arbitrary cleanup methods.
    • Targeted Removal: Janitor:Remove("Index") allows removing a specific item by its index.
    • Removal without Cleanup: Janitor:RemoveNoClean("Index") allows removing an item from the Janitor without triggering its cleanup method.
    | Operation | **Janitor** | **Maid** | **Trove** |
    | :--- | :--- | :--- | :--- |
    | Adding an Instance | `Janitor:Add(Instance)` | `Maid:GiveTask(Instance)` | `Trove:Add(Instance)` |
    | Adding with index | `Janitor:Add(Instance, false, "Index")` | `Maid.Index = Instance` | **CAN NOT DO THIS** |
    | Adding with cleanup method | `Janitor:Add(Tween, "Cancel")` | **CAN NOT DO THIS** | `Trove:Add(Tween, "Cancel")` |
    | Removing an Instance | `Janitor:Remove("Index")` | `Maid.Index = nil` | **CAN NOT DO THIS** |
    | Removing without cleanup | `Janitor:RemoveNoClean("Index")` | **CAN NOT DO THIS** | **CAN NOT DO THIS** |