Sleeper is a simple module that makes it easy to pause and resume your code using coroutines.
It’s perfect for situations where you want to avoid using while or repeat loops to check for conditions, and it’s useful for managing multiple coroutines.
For devs who prefer to copy and paste instead of downloading the model.
--!strict
-- [[Modules]]
local InsertOrder do
-- [[Types]]
type InsertOrderClass = {
["__index"]: InsertOrderClass,
["__len"]: (self: InsertOrderObject) -> number,
["New"]: () -> InsertOrderObject,
["Add"]: (self: InsertOrderObject, Value: any, Index: any) -> (),
["Remove"]: (self: InsertOrderObject, Index: any) -> (),
["Iterate"]: (self: InsertOrderObject) -> () -> (any, any),
["Clear"]: (self: InsertOrderObject) -> (),
["GetValueWithIndex"]: (self: InsertOrderObject, Index: any) -> any,
["GetIndexWithPosition"]: (self: InsertOrderObject, Position: number) -> any,
["Destroy"]: (self: InsertOrderObject) -> ()
}
type InsertOrderObject = typeof(
setmetatable(
{} :: {
["_Data"]: {[any]: any},
["_Indexes"]: {[number]: any}
},
{} :: InsertOrderClass
)
)
-- [[Class]]
local InsertOrderClass = {} :: InsertOrderClass
-- [[Metamethods]]
InsertOrderClass.__index = InsertOrderClass
InsertOrderClass.__len = function(self: InsertOrderObject)
return #self._Indexes
end
-- [[Utils]]
local function Clear(Table: {[any]: any}): ()
for Index, Value in pairs(Table) do
if type(Value) == "table" then
Clear(Value)
end
Table[Index] = nil
end
end
-- [[Constructor]]
function InsertOrderClass.New(): InsertOrderObject
return setmetatable({
_Data = {},
_Indexes = {}
}, InsertOrderClass)
end
-- Method to add
function InsertOrderClass.Add(self: InsertOrderObject, Value: any, Index: any): ()
assert(Index, "Index cannot be nil")
if not self._Data[Index] then
self._Indexes[#self._Indexes + 1] = Index
end
self._Data[Index] = Value
end
-- Method to remove with a index
function InsertOrderClass.Remove(self: InsertOrderObject, Index: any): ()
assert(Index, "Index cannot be nil")
self._Data[Index] = nil
local Position = table.find(self._Indexes, Index)
if Position then
table.remove(self._Indexes, Position)
end
end
-- Method to iterate
function InsertOrderClass.Iterate(self: InsertOrderObject): () -> (any, any)
local CurrentIndex = 0
return function()
CurrentIndex = CurrentIndex + 1
local Index = self._Indexes[CurrentIndex]
if Index ~= nil then
return Index, self._Data[Index]
end
return nil
end
end
-- Method to clear Data and _Indexes
function InsertOrderClass.Clear(self: InsertOrderObject): ()
table.clear(self._Data)
table.clear(self._Indexes)
end
-- Method to get a value with a index
function InsertOrderClass.GetValueWithIndex(self: InsertOrderObject, Index: any): any
assert(Index, "Index cannot be nil")
local Value = self._Data[Index]
return Value
end
-- Method to get a index with a position
function InsertOrderClass.GetIndexWithPosition(self: InsertOrderObject, Position: number): any
assert(Position, "Position cannot be nil")
return self._Indexes[Position]
end
-- Method to destroy
function InsertOrderClass.Destroy(self: InsertOrderObject): ()
Clear(self)
setmetatable(self, nil)
end
-- [[Returning]]
InsertOrder = InsertOrderClass
end
-- [[Types]]
type SleeperClass = {
["__index"]: SleeperClass,
["New"]: () -> SleeperObject,
["Sleep"]: (self: SleeperObject, Index: any?) -> ... any,
["WakeUpByIndex"]: (self: SleeperObject, Index: any, ... any) -> (),
["WakeUpByPosition"]: (self: SleeperObject, Position: number, ... any) -> (),
["WakeUpAll"]: (self: SleeperObject, ... any) -> (),
["Destroy"]: (self: SleeperObject) -> ()
}
type SleeperObject = typeof(
setmetatable(
{} :: {
["_RunningCoroutines"]: typeof(InsertOrder.New())
},
{} :: SleeperClass
)
)
-- [[Class]]
local Sleeper = {} :: SleeperClass
-- [[Metamethods]]
Sleeper.__index = Sleeper
-- [[Constructor]]
function Sleeper.New(): SleeperObject
return setmetatable({
_RunningCoroutines = InsertOrder.New()
}, Sleeper)
end
-- Method to yield
function Sleeper.Sleep(self: SleeperObject, Index: any?): ... any
self._RunningCoroutines:Add(coroutine.running(), Index or #self._RunningCoroutines + 1)
return coroutine.yield()
end
-- Method to continue a yield
function Sleeper.WakeUpByIndex(self: SleeperObject, Index: any, ...: any): ()
assert(Index, "Index cannot be nil")
local RunningCoroutine = self._RunningCoroutines:GetValueWithIndex(Index)
if not RunningCoroutine then
warn(`Index {Index} doesn't exist`)
return
end
task.spawn(RunningCoroutine, ...)
self._RunningCoroutines:Remove(Index)
end
function Sleeper.WakeUpByPosition(self: SleeperObject, Position: number, ...: any): ()
assert(Position, "Position cannot be nil")
local PositionIndex = self._RunningCoroutines:GetIndexWithPosition(Position)
local RunningCoroutine = PositionIndex and self._RunningCoroutines:GetValueWithIndex(PositionIndex)
if not RunningCoroutine then
warn(`Position {Position} doesn't exist`)
return
end
task.spawn(RunningCoroutine, ...)
self._RunningCoroutines:Remove(PositionIndex)
end
-- Method to continue all yields
function Sleeper.WakeUpAll(self: SleeperObject, ...: any): ()
for _, RunningCoroutine in self._RunningCoroutines:Iterate() do
task.spawn(RunningCoroutine, ...)
end
self._RunningCoroutines:Clear()
end
-- Method to destroy
function Sleeper.Destroy(self: SleeperObject): ()
self._RunningCoroutines:Destroy()
table.clear(self)
setmetatable(self, nil)
end
-- [[Returning]]
return Sleeper
Features
Sleeper.New()
— creates a new Sleeper object
SleeperObject:Sleep(index?)
— pauses the current coroutine, optionally storing it under a custom index
SleeperObject:WakeUpByIndex(index, ...)
— resumes the coroutine that was paused using the given index
SleeperObject:WakeUpByPosition(position, ...)
— resumes the coroutine based on the order it was added
SleeperObject:WakeUpAll(...)
— resumes all paused coroutines
SleeperObject:Destroy()
— cleans up the object
Examples
Example #1: Wait for Condition
local Sleeper = require(...)
local SleeperObject = Sleeper.New()
local condition = false
task.delay(3, function()
condition = true
SleeperObject:WakeUpByIndex("Condition")
end)
SleeperObject:Sleep("Condition")
print(condition) -- true
This replaces code like:
while not condition do task.wait() end
Example #2: Simple Module Loader
local Sleeper = require(...)
local sleepers = {}
local loadedModules = {}
local ModuleLoader = {}
function ModuleLoader.Get(name)
if not loadedModules[name] then
sleepers[name] = sleepers[name] or Sleeper.New()
sleepers[name]:Sleep()
end
return loadedModules[name]
end
function ModuleLoader.Init(modules)
for _, moduleScript in ipairs(modules) do
local name = moduleScript.Name
loadedModules[name] = require(moduleScript)
if sleepers[name] then
sleepers[name]:WakeUpAll()
sleepers[name]:Destroy()
sleepers[name] = nil
end
end
end
return ModuleLoader
Other Use Cases
- Queue systems
You can use this module for many other things beyond just these examples. It’s flexible and can be adapted to different situations where you need to manage coroutines.
- Yes🥰
- No😈