Coroutines drive game scripting, async I/O in OpenResty and Neovim, and elegant generators.
A coroutine is a function with its own stack that can pause itself and be resumed later. It is cooperative: only one runs at a time, and it runs until it explicitly yields.
coroutine.create(f)makes a coroutine (typethread) without running it.coroutine.resume(co, ...)starts or continues it. It returnstrueplus the values passed toyield(or returned at the end), orfalse, errif the coroutine raised an error.coroutine.yield(...)suspends the current coroutine; the values become resume's results, and the arguments of the nextresumebecome yield's return values.coroutine.status(co)issuspended,running,normalordead.coroutine.wrap(f)returns a function that resumes on each call and re-raises errors, perfect for generators used in a generic for.
Because the stack is preserved, a coroutine can yield from deep inside nested calls. That lets you write sequential-looking code (wait for an animation, then for input, then for a timer) while a scheduler resumes the coroutine when each event happens. OpenResty and many Neovim plugins turn callback APIs into straight-line code exactly this way.
coroutine.close(co) (5.4) kills a suspended coroutine and runs its pending to-be-closed variables. Yielding across a C call boundary only works when the C function supports continuations.
Example
local function numbers(limit)
return coroutine.wrap(function()
for i = 1, limit do coroutine.yield(i, i * i) end
end)
end
for n, sq in numbers(3) do io.write(n, ":", sq, " ") end
print() --> 1:1 2:4 3:9
local co = coroutine.create(function(a)
print("got", a) --> got 1
local b = coroutine.yield(a + 1)
print("got", b) --> got 10
return "done"
end)
print(coroutine.resume(co, 1)) --> true 2
print(coroutine.status(co)) --> suspended
print(coroutine.resume(co, 10)) --> true done
print(coroutine.status(co)) --> dead
print(coroutine.resume(co)) --> false cannot resume dead coroutineRemember
resumepasses values in;yieldpasses values out, and vice versacoroutine.wrapgives a function, ideal for generators- Coroutines are cooperative and keep their whole call stack
Watch out for
- Ignoring resume's first return value: errors inside a coroutine are returned, not raised.