Creating Coroutines
Create coroutines with coroutine.create and coroutine.wrap.
Creating Coroutines is a free Lua Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Lua Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What is a Coroutine?
A coroutine is a function that can pause its execution (yield) and later resume from where it left off. Unlike threads, coroutines are cooperative — only one runs at a time, and they explicitly yield control. Lua coroutines have their own stack and local variables.
local co = coroutine.create(function()
print("start")
coroutine.yield()
print("resumed")
coroutine.yield()
print("done")
end)
print(coroutine.status(co)) -- suspended
coroutine.resume(co) -- start
print(coroutine.status(co)) -- suspended
coroutine.resume(co) -- resumedcoroutine.create vs coroutine.wrap
coroutine.create(fn) creates a coroutine and returns a coroutine object (type thread). coroutine.wrap(fn) creates a coroutine and returns a function that, when called, resumes it. wrap is simpler but hides the coroutine object.
-- create: returns coroutine object
local co = coroutine.create(function(x)
return x * 2
end)
local ok, val = coroutine.resume(co, 5)
print(ok, val) -- true 10
-- wrap: returns a resumable function
local gen = coroutine.wrap(function(x)
return x * 3
end)
print(gen(5)) -- 15Coroutine Lifecycle
A coroutine has four states: suspended (not running), running (currently executing), dead (returned or errored), and normal (paused because it resumed another coroutine). Check state with coroutine.status(co).
local co = coroutine.create(function()
coroutine.yield()
end)
print(coroutine.status(co)) -- suspended
coroutine.resume(co) -- runs until yield
print(coroutine.status(co)) -- suspended again
coroutine.resume(co) -- runs until end
print(coroutine.status(co)) -- deadRunning Multiple Coroutines
Create multiple coroutines and interleave them by resuming each in turn. This simulates concurrent execution (cooperative multitasking). Each coroutine keeps its own state between yields.
local function worker(name, steps)
for i = 1, steps do
print(name .. ": step " .. i)
coroutine.yield()
end
end
local co1 = coroutine.create(function() worker("A", 3) end)
local co2 = coroutine.create(function() worker("B", 3) end)
for _ = 1, 3 do
coroutine.resume(co1)
coroutine.resume(co2)
end
-- A:1, B:1, A:2, B:2, A:3, B:3coroutine.wrap Iteration
coroutine.wrap is commonly used to create iterators. The returned function is called each time the iterator advances. When the coroutine body ends, the function raises an error — use pcall or check status if needed.
local function range(n)
return coroutine.wrap(function()
for i = 1, n do
coroutine.yield(i)
end
end)
end
for v in range(5) do
io.write(v .. " ")
end
print() -- 1 2 3 4 5Error in Coroutines
If a coroutine raises an error, coroutine.resume returns false followed by the error message. The coroutine transitions to the dead state. A dead coroutine cannot be resumed again.
local co = coroutine.create(function(x)
if x < 0 then error("negative input: " .. x) end
return math.sqrt(x)
end)
local ok, val = coroutine.resume(co, -1)
print(ok, val) -- false input:2: negative input: -1
print(coroutine.status(co)) -- dead
-- Can't resume a dead coroutine:
ok, val = coroutine.resume(co)
print(ok, val) -- false cannot resume dead coroutineCoroutine Identity
coroutine.running() returns the currently running coroutine and a boolean indicating if it's the main thread. Use this inside a coroutine to get a reference to itself.
local co = coroutine.create(function()
local self, isMain = coroutine.running()
print("type:", type(self)) -- thread
print("isMain:", isMain) -- false
print("status:", coroutine.status(self)) -- running
end)
coroutine.resume(co)
-- In main thread:
local main, isMain = coroutine.running()
print("main isMain:", isMain) -- trueInfinite Generator
A coroutine body can contain an infinite loop with yields inside — the generator produces values on demand without terminating. This is a key use case: lazy, infinite sequences.
local function naturals(start)
return coroutine.wrap(function()
local n = start or 1
while true do
coroutine.yield(n)
n = n + 1
end
end)
end
local gen = naturals(1)
for i = 1, 8 do
io.write(gen() .. " ")
end
print() -- 1 2 3 4 5 6 7 8Coroutines as Tasks
A basic task scheduler: store coroutines in a queue and resume each in turn. When a coroutine yields, it goes back in the queue. When it dies, it's removed. This is a minimal cooperative scheduler.
local tasks = {}
local function spawn(fn)
tasks[#tasks+1] = coroutine.create(fn)
end
local function run()
while #tasks > 0 do
local alive = {}
for _, co in ipairs(tasks) do
coroutine.resume(co)
if coroutine.status(co) ~= "dead" then
alive[#alive+1] = co
end
end
tasks = alive
end
end
spawn(function() for i=1,3 do print("T1:"..i); coroutine.yield() end end)
spawn(function() for i=1,3 do print("T2:"..i); coroutine.yield() end end)
run()Coroutine Memory
Each coroutine has its own stack. Stacks can grow large for deep call chains. In high-performance scenarios, avoid creating thousands of simultaneously live coroutines. Reuse coroutines when possible, or use pools.
-- Coroutine pool concept
local pool = {}
local function getCoroutine(fn)
-- Reuse dead coroutine slots
for i, co in ipairs(pool) do
if coroutine.status(co) == "dead" then
pool[i] = coroutine.create(fn)
return pool[i]
end
end
local co = coroutine.create(fn)
pool[#pool+1] = co
return co
end
print("Pool pattern for coroutine reuse")Quick Check
What state is a coroutine in immediately after coroutine.create(fn)?
Recap: Creating Coroutines
Summary:
coroutine.create(fn)→ coroutine object;wrap(fn)→ resumable function- States: suspended → running → suspended/dead
coroutine.resume(co, ...)starts/resumes- Errors during resume: false + error message; coroutine → dead
- Infinite generators via
while true do yield() end - Use for cooperative multitasking and lazy sequences
Frequently asked questions
Is the “Creating Coroutines” lesson free?
Yes — the full text of “Creating Coroutines” is free to read here on the web, and the Lua Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Lua Academy course, upgrade to CoddyKit PRO.
What will I learn in “Creating Coroutines”?
Create coroutines with coroutine.create and coroutine.wrap. You practise Lua Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Lua Academy?
No prior experience is required. Lua Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Creating Coroutines” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Lua Academy lesson?
Yes. Every Lua Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Creating Coroutines
- resume and yield
- Coroutine Status
- Producer-Consumer Pattern