- π High Performance: ~238,000 tasks/second throughput
- π― C#-like API: Familiar async/await pattern
- π Cancellation Support: Cooperative cancellation tokens
- π Task Status Tracking: Complete task lifecycle visibility
- π§ͺ Well Tested: Comprehensive unit and memory tests
- πΎ Memory Safe: No memory leaks detected
- π§ Production Ready: Battle-tested and optimized
Simply copy async.lua to your project:
curl -O https://raw.githubusercontent.com/dlqw/LuaAsync/main/async.luaOr require it directly in your project.
require("async")
-- Initialize the async system
Async.Init()
-- Update function (call each frame from your game loop)
function Tick(deltaTime)
Async.Update(deltaTime)
end
-- Create and run async tasks
local task = Task.Run(function()
print("Task started!")
Await(Task.Delay(1000)) -- Wait 1 second
print("Task finished!")
return "result"
end)
-- Add completion callback
task:OnCompleted(function()
print("Task result:", task._result)
end)-- Run a task (root task - creates new task group)
Task.Run(function()
-- Your async code here
end, cancellationToken)
-- Create task and start manually
local task = Task.new(function()
return "result"
end, cancellationToken)
task:Start()
-- Create already-completed task
local task = Task.FromResult("immediate result")
-- Create sub-task (executes in current task group)
Task.RunAsSub(function()
-- Runs within parent task group
end, cancellationToken)-- Wait for a delay
Await(Task.Delay(1000)) -- 1000ms
-- Wait until next frame
Await(Task.NextFrame())
-- Wait for a condition
Await(Task.Until(function()
return Player.health <= 0
end))
-- Wait for multiple tasks (all)
local results = {Await(Task.WhenAll(nil, task1, task2, task3))}
-- Wait for multiple tasks (any)
local firstIndex = Await(Task.WhenAny(nil, task1, task2))-- Get task status
local status = task:GetStatus()
-- Possible values:
-- TaskStatus.Created
-- TaskStatus.Running
-- TaskStatus.Completed
-- TaskStatus.Cancelled
-- TaskStatus.Faulted
-- Access result
if status == TaskStatus.Completed then
print("Result:", task._result)
end
-- Add completion callback
task:OnCompleted(function()
print("Task completed!")
end)
-- Fire-and-forget (starts task as root)
Task.Run(function()
-- This runs without awaiting
end):Forget()-- Create cancellation token source
local cts = CancellationTokenSource.new()
-- Get token
local token = cts:GetToken()
-- Pass to tasks
local task = Task.Run(function()
Await(Task.Delay(5000, token))
end, token)
-- Cancel from anywhere
cts:Cancel()
-- Check if cancelled
if token:IsCancellationRequested() then
print("Cancelled!")
end-- Convert any Waitable to a Task
local delayWaitable = Task.Delay(1000)
local delayTask = delayWaitable:ToTask(optionalToken)require("async")
Async.Init()
function Tick(deltaTime)
Async.Update(deltaTime)
end
-- Example 1: Simple delay
local task1 = Task.Run(function()
print("Loading...")
Await(Task.Delay(1000))
print("Loaded!")
end)
-- Example 2: Multiple tasks with WhenAll
local task2 = Task.new(function()
Await(Task.Delay(500))
return "Hello"
end)
local task3 = Task.new(function()
Await(Task.Delay(500))
return "World"
end)
Task.Run(function()
local results = {Await(Task.WhenAll(nil, task2, task3))}
print(results[1], results[2]) -- "Hello World"
end)
-- Example 3: Cancellation
local cts = CancellationTokenSource.new()
local longTask = Task.Run(function()
for i = 1, 100 do
if cts:GetToken():IsCancellationRequested() then
print("Task cancelled!")
return
end
Await(Task.Delay(100))
print("Progress:", i .. "%")
end
end, cts:GetToken())
-- Cancel after 2 seconds
Task.Run(function()
Await(Task.Delay(2000))
cts:Cancel()
end):Forget()
-- Example 4: Conditional waiting
local player = { health = 100 }
Task.Run(function()
-- Wait for player death or timeout
local index = Await(Task.WhenAny(nil,
Task.Delay(5000):ToTask(),
Task.Until(function() return player.health <= 0 end):ToTask()
))
if index == 1 then
print("Timeout!")
else
print("Player died!")
end
end):Forget()Run the test suite:
# Unit tests (15 tests covering all features)
lua test_async.lua
# Memory leak tests
lua memory_test.lua
# Performance benchmarks
lua benchmark_async.lua$ lua test_async.lua
β All 15 tests passed!
$ lua memory_test.lua
β ALL MEMORY TESTS PASSED - No significant leaks detectedPerformance data based on 5 consecutive benchmark runs on Windows/Cygwin with 10,000 iterations:
| Metric | Average | Range | Rating |
|---|---|---|---|
| Task Creation | 0.0038ms | 0.0033-0.0042ms | βββββ |
| Scheduling Throughput | 215,634 tasks/sec | 163,934-256,410 | βββββ |
| FromResult | 2,173,913 tasks/sec | 2,000,000+ | βββββ |
| Memory Leaks | None detected | - | βββββ |
=== 5 Consecutive Benchmark Runs ===
Run 1: 227,273 tasks/sec, 0.0033ms/task
Run 2: 222,222 tasks/sec, 0.0042ms/task
Run 3: 163,934 tasks/sec, 0.0041ms/task (min)
Run 4: 208,333 tasks/sec, 0.0038ms/task
Run 5: 256,410 tasks/sec, 0.0038ms/task (max)
Average: 215,634 tasks/sec, 0.0038ms/task
StdDev: Β±32,610 tasks/sec (Β±15% variance)
Note: Performance varies based on system load, Lua version, and hardware. Results shown are typical values on a modern Windows system.
- Game Development: Asynchronous game logic, animations, AI
- Network Programming: Non-blocking HTTP requests, websockets
- File I/O: Asynchronous file operations
- UI Programming: Smooth animations, transitions
- Background Tasks: Data processing, calculations
Task.Run(function()
print("Parent task")
Task.RunAsSub(function()
print("Child task 1")
Await(Task.Delay(100))
end)
Task.RunAsSub(function()
print("Child task 2")
Await(Task.Delay(100))
end)
-- Wait for all children to complete
coroutine.yield()
print("All children done")
end)local task = Task.Run(function()
for i = 1, 10 do
coroutine.yield()
end
end)
while task:GetStatus() ~= TaskStatus.Completed do
print("Task is still running...")
Tick(0.016)
end
print("Task completed with status:", task:GetStatus())local task = Task.Run(function()
if some_error then
error("Something went wrong!")
end
return "success"
end)
task:OnCompleted(function()
if task:GetStatus() == TaskStatus.Faulted then
print("Task failed!")
else
print("Task succeeded:", task._result)
end
end)| Function | Description |
|---|---|
Task.new(func, token) |
Create a new task |
Task.Run(func, token) |
Create and run a task (root) |
Task.RunAsSub(func, token) |
Create and run as sub-task |
Task.Delay(ms, token) |
Create a delay waitable |
Task.NextFrame(token) |
Wait until next frame |
Task.Until(condition, token) |
Wait for condition |
Task.WhenAll(token, ...) |
Wait for all tasks |
Task.WhenAny(token, ...) |
Wait for first task |
Task.FromResult(value) |
Create completed task |
| Method | Description |
|---|---|
task:Start() |
Start as root task |
task:StartAsSub() |
Start as sub-task |
task:Forget() |
Start without awaiting (uses Start) |
task:OnCompleted(callback) |
Add completion callback |
task:GetStatus() |
Get current status |
task:Complete() |
Mark as complete (internal) |
| Method/Property | Description |
|---|---|
CancellationTokenSource.new() |
Create token source |
source:Cancel() |
Request cancellation |
source:GetToken() |
Get the token |
token:IsCancellationRequested() |
Check if cancelled |
| Value | Description |
|---|---|
TaskStatus.Created |
Task created, not started |
TaskStatus.Running |
Task is executing |
TaskStatus.Completed |
Task completed successfully |
TaskStatus.Cancelled |
Task was cancelled |
TaskStatus.Faulted |
Task encountered an error |
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by C#'s Task-based Asynchronous Pattern
- Built with Lua coroutines
For issues, questions, or suggestions, please open an issue on GitHub.
Made with β€οΈ by the LuaAsync community