Skip to content

Instantly share code, notes, and snippets.

@artfwo
Last active November 26, 2025 23:38
Show Gist options
  • Select an option

  • Save artfwo/e8b9bd51e0043a54b0c0d65d75c332d6 to your computer and use it in GitHub Desktop.

Select an option

Save artfwo/e8b9bd51e0043a54b0c0d65d75c332d6 to your computer and use it in GitHub Desktop.
Minimal example of scheduling async tasks in pure Lua
-- Minimal example of scheduling async tasks in pure Lua
local Future = {}
Future.__index = Future
function Future.new()
local future = setmetatable({}, Future)
future.done = false
future.result = nil
future.waiters = {}
return future
end
function Future:set_result(result)
if self.done then
return
end
self.done = true
self.result = result
for _, co in ipairs(self.waiters) do
local success, err = coroutine.resume(co, result)
if not success then
error(err)
end
end
self.waiters = {}
end
function Future:__tostring()
if self.done then
return string.format("<Future: done (%s)>", tostring(self.result))
else
return "<Future: pending>"
end
end
local function await(future)
if future.done then
return future.result
end
local co = coroutine.running()
assert(co, "await() must be called from inside a coroutine")
table.insert(future.waiters, co)
return coroutine.yield()
end
local function run(fn)
local future = Future.new()
local co = coroutine.create(function()
local result = fn()
future:set_result(result)
end)
local success, err = coroutine.resume(co)
if not success then
error(err)
end
return future
end
-- Scheduler --
local TestScheduler = {}
TestScheduler.__index = TestScheduler
function TestScheduler.new()
local test_scheduler = setmetatable({}, TestScheduler)
test_scheduler.time = 0
test_scheduler.futures = {}
return test_scheduler
end
function TestScheduler:schedule_future_at_time(tick, future)
self.futures[tick] = self.futures[tick] or {}
table.insert(self.futures[tick], future)
end
function TestScheduler:tick()
self.time = self.time + 1
local futures = self.futures[self.time]
if futures then
for _, future in ipairs(futures) do
future:set_result(self.time)
end
self.futures[self.time] = nil
end
end
local test_scheduler = TestScheduler.new()
local function sleep(ticks)
local future = Future.new()
test_scheduler:schedule_future_at_time(test_scheduler.time + ticks, future)
return future
end
-- Example task
local function test_task()
local b
run(function()
await(sleep(7))
print("at 7 1 1", test_scheduler.time)
end)
run(function()
await(sleep(7))
print("at 7 2 1", test_scheduler.time)
await(sleep(1))
print("at 7 2 2", test_scheduler.time)
end)
print(test_scheduler.time, "task A", tostring(b))
b = await(sleep(5))
print(test_scheduler.time, "task A", tostring(b))
b = await(sleep(5))
print(test_scheduler.time, "task A", tostring(b))
await(run(function()
print(test_scheduler.time, "subtask B", tostring(b))
b = await(sleep(3))
print(test_scheduler.time, "subtask B", tostring(b))
end))
b = await(sleep(3))
print(test_scheduler.time, "task A", tostring(b))
end
run(test_task)
repeat
test_scheduler:tick()
until test_scheduler.time > 20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment