Skip to content

Bag

local Bag = require("@game/ReplicatedStorage/Twill").Bag
local bag = Bag.new()
bag:Connect(part.Touched, onTouched)
bag:Add(model)
bag:Destroy()

A bag closes newest first. Anything added later is closed before what it was built on.

Every cleanup runs inside its own pcall. One that raises is collected and the drain continues, so a single failure cannot strand the entries queued behind it. Failures are reported through the log after the bag is empty, one line each.

Adding to a bag from inside one of its own cleanups is allowed. The addition is closed in the same pass.

Destroy on a bag that is already destroyed does nothing.

Add works out how to close most things on its own.

GivenClosed by
InstanceDestroy
RBXScriptConnectionDisconnect
functionCalling it
threadtask.cancel, skipped when the thread is already dead
tableThe first of Destroy, Disconnect, destroy, disconnect it carries

Anything else needs the closing method named. Pass the method name as the second argument, or true for a function or thread.

A thread cancelling itself is deferred rather than cancelled in place.

The third argument to Add is a key. Adding again under the same key closes whatever held it before.

bag:Add(track, nil, "walk")
bag:Add(otherTrack, nil, "walk")

The first track is closed when the second takes the name. Get reads what a key holds without letting it out.

AttachTo turns ownership the other way round: the instance going closes the bag, rather than the bag closing the instance.

An instance that is not in game closes the bag immediately, and AttachTo answers nil. A bag can be attached to one instance at a time; attaching again detaches first.

[Server] | [Client]

Makes an empty bag.

function Bag.new(): Bag

[Server] | [Client]

Answers whether a value is one of these bags.

function Bag.Is(value: any): boolean

[Server] | [Client]

Holds an object until the bag closes, and hands it straight back.

function Bag:Add<T>(object: T, method: (string | boolean)?, key: any?): T

Parameters

NameTypeDescription
objectTAn instance, connection, function, thread, or object.
method(string | boolean)?The name of the method that closes it, or true for a function or thread. Worked out when left out.
keyany?A name for it. Closes whatever held that name before.

Returns

T - The object that was passed in.

Throws when the bag has been destroyed, when nothing was passed, when the bag is passed to itself, and when nothing says how the object closes.

[Server] | [Client]

Connects a signal and holds the connection.

function Bag:Connect(signal: any, callback: (...any) -> ()): any

Takes an RBXScriptSignal or any object carrying Connect, which includes Signal. Throws on anything else.

[Server] | [Client]

Connects a signal for one firing, letting the entry go the moment it fires.

function Bag:Once(signal: any, callback: (...any) -> ()): any

The entry leaves the bag when it fires, so a bag holding many one-shot listeners does not grow.

[Server] | [Client]

Runs a callback on its own thread, cancelled if it is still going when the bag closes.

function Bag:Task(callback: (...any) -> (), ...: any): thread

[Server] | [Client]

Runs a callback later, cancelled outright if the bag closes first.

function Bag:Delay(seconds: number, callback: (...any) -> (), ...: any): thread

[Client]

Binds a callback to render step, unbound by name when the bag closes.

function Bag:Bind(name: string, priority: number, callback: (delta: number) -> ())

Throws on the server.

[Server] | [Client]

Clones an instance and holds the copy. The original is left alone.

function Bag:Clone<T>(instance: T & Instance): T

[Server] | [Client]

Makes a bag inside this one, closing when this one does.

function Bag:Extend(): Bag

[Server] | [Client]

Returns what is held under a name, without letting it out.

function Bag:Get(key: any): any

[Server] | [Client]

Closes one entry now and lets it out, leaving the rest of the bag alone.

function Bag:Remove(what: any): boolean

Takes the key it was named with, or the object itself. Answers false when the bag was not holding it.

[Server] | [Client]

Lets one entry out without closing it, handing ownership back to the caller.

function Bag:Release(what: any): boolean

[Server] | [Client]

Returns how many things the bag is holding.

function Bag:Count(): number

[Server] | [Client]

Answers whether the bag is holding nothing at all.

function Bag:IsEmpty(): boolean

[Server] | [Client]

Answers whether the bag has been destroyed and can no longer be added to.

function Bag:IsDestroyed(): boolean

[Server] | [Client]

Closes the bag when an instance goes.

function Bag:AttachTo(instance: Instance): any

Returns

any - The connection watching the instance, or nil when the bag closed at once because the instance is not in game.

Throws when the value is not an instance.

[Server] | [Client]

Stops watching whatever instance the bag was attached to, leaving the bag open.

function Bag:Detach()

[Server] | [Client]

Closes everything held and leaves the bag ready to be used again.

function Bag:Clean()

[Server] | [Client]

Closes everything held and makes the bag unusable.

function Bag:Destroy()