Skip to content
AgileLogics
6 min read

Coroutines

Prerequisites: Iterators

A coroutine is a function that can pause itself with yield and later resume where it left off. Only one runs at a time — it's cooperative multitasking, not parallelism.

local co = coroutine.create(function(a, b)
  print("started with", a, b)
  local c = coroutine.yield(a + b)
  print("resumed with", c)
  local d, e = coroutine.yield(c * 2)
  print("resumed with", d, e)
  return "finished"
end)

print(coroutine.resume(co, 1, 2))
print(coroutine.status(co))
print(coroutine.resume(co, 10))
print(coroutine.resume(co, "x", "y"))
print(coroutine.status(co))
print(coroutine.resume(co))
started with	1	2
true	3
suspended
resumed with	10
true	20
resumed with	x	y
true	finished
dead
false	cannot resume dead coroutine
  • coroutine.resume(co, ...) starts or continues the coroutine. It returns true plus the values passed to yield (or returned at the end), or false plus an error.
  • Values passed to resume become the results of the yield that paused it.
  • Status is suspended, running, or dead.

Generators with coroutine.wrap

local function fibonacci(limit)
  return coroutine.wrap(function()
    local a, b = 0, 1
    while a <= limit do
      coroutine.yield(a)
      a, b = b, a + b
    end
  end)
end

for n in fibonacci(100) do io.write(n, " ") end
print()
0 1 1 2 3 5 8 13 21 34 55 89

coroutine.wrap returns a function that resumes the coroutine each time it's called — a perfect iterator. Writing the loop "naturally" with yield is often much easier than keeping state in a closure.

Producers and consumers

local function producer()
  return coroutine.wrap(function()
    for _, line in ipairs({"alpha", "beta", "gamma"}) do
      coroutine.yield(line)
    end
  end)
end

local function filter(source, pred)
  return coroutine.wrap(function()
    for item in source do
      if pred(item) then coroutine.yield(item) end
    end
  end)
end

for item in filter(producer(), function(s) return s:find("a$") end) do
  print(item:upper())
end
ALPHA
BETA
GAMMA

Each stage pulls items one at a time from the previous one — a lazy pipeline.