task+
A simple and useful upgrade for the native task library.
task+ is a lightweight module designed to upgrade Roblox’s native task functions. It protects your game from multi-threading overheads and lets you link your functions into clean chains (main feature) so you don’t have to write messy code.
You can set it up like this:
const task = require(path.to.stupid["task+"]);
What it does
- Function Chaining: No more messy nested code. You can run functions one after another using simple methods like
:andCall(),:andThen(),:andCatch(), and:andClear(). - Safe Multi-threading: The module automatically knows whether it is in a parallel or serial thread, protecting your game from redundant task scheduler overheads (like setting lua_State to serial when it is already synchronized).
- Optimized Performance: All chained functions run sequentially inside a single thread context.
- Global Thread Manager: Using
task.Threads, you can check whether a thread belongs to task+, see if it is done, or completely wipe it from memory from any other script. - Full Autocomplete: Complete support for Luau typing so you won’t get any annoying yellow lines in Roblox Studio, guh.
Code Examples
1. Basic Chaining (Run functions one after another)
local function printHello()
print("Hello")
end
local function printWorld()
print("World")
end
--// waits 5 seconds, prints "Hello", and prints "World" right after it finishes
task.delay(5, printHello):andCall(printWorld)
2. Passing Data & Catching Errors (Simple Pipeline)
local function getNumber()
return 10 --// passes this number to the next step
end
task.spawn(getNumber)
:andThen(function(myNumber)
print("Got number:", myNumber) --// prints 10
return myNumber * 2 --// passes 20 to the next step
end)
:andThen(function(newNumber)
if newNumber > 15 then
error("Number is too big!") --// triggers an error
end
end)
:andCatch(function(err)
warn("Caught a bug:", err) --// runs because an error happened above
end)
:andClear() --// completely cleans up the memory
3. Safe Calls (Catching errors instantly)
local function brokenFunction()
error("imagine error here")
end
--// ':andSafeCall' runs a function safely and puts results straight into variables
local success, result = task.spawn(brokenFunction):andSafeCall(function()
print("This runs inside pcall")
end)
print(success) --// prints false
4. Managing Threads from the Outside
local myThread = task.delay(10, function() print("Done") end)
--// check if this is a task+ object and if it is completed from another script
if task.Threads.IsTaskPlus(myThread) and task.Threads.IsCompleted(myThread) then
task.Threads.Clear(myThread) --// Forcefully deletes the thread from memory
end
Thread Synchronization Note
task+ utilizes a fast SharedTable trap to check thread states instantly:
if parallel_states.IsParallel() then
task.synchronize()
end
The built-in thread context changers (task.synchronize / task.desynchronize) are already able to detect the current thread context automatically.
Why task+ instead of standard Promises?
Standard Promise libraries (like evaera’s) are incredibly robust and reliable. However, they are heavy because they port the entire JavaScript Promise ecosystem into Roblox.
Here is why you might want to use task+ instead:
-
Thread Switch Optimization: Calling
task.synchronize()ortask.desynchronize()multiple times causes microsecond overhead because the engine forces context switches through the Task Scheduler. task+ uses a fastSharedTabletrap to check the thread state first, executing the synchronization methods only when actually necessary to save CPU cycles. -
Single-Thread Performance: Instead of spawning a new coroutine for every single link in the chain (why?), task+ executes chained methods sequentially within the same thread context. This significantly reduces Garbage Collector and Task Scheduler pressure.
-
Native Luau Feel: With methods like
:andSafeCall(), you can instantly return values back into standard Lua tuples (local success, result = ...) instead of being forced into continuous callback nesting.