Coroutines

By the end of this lesson you'll create and drive coroutines with create , resume , yield , status , and wrap , pass values in and out across yield / resume , build generator and producer-consumer patterns, understand why coroutines are cooperative rather than OS threads, and see why game loops love them.

Learn Coroutines in our free Lua course — an interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free Lua course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

A coroutine is like reading a book with a bookmark . You read for a while, then slip the bookmark in and put the book down to do something else (that's yield ). Later you pick it up and carry on from the exact page you left (that's resume ) — no need to start over. Unlike juggling several books at once on different tables (true parallel threads), you only ever read one book at a time, and you choose precisely when to set it down. That cooperative, "pause exactly here and continue exactly there" behaviour is the whole idea.

1. Create, Resume, Yield & Status

A coroutine is a function that can pause itself with coroutine.yield and later continue from that exact point. You build one with coroutine.create(fn) , which returns a coroutine that starts suspended . Each coroutine.resume(co) runs it until the next yield , the end of the function, or an error. coroutine.status(co) tells you where it stands — suspended , running , normal , or dead — and a finished (dead) coroutine can't be resumed again. Read and run this example.

2. Passing Values In and Out

Coroutines are a two-way channel . On the very first resume , the extra arguments become the body function's parameters. After that, whatever you pass to resume becomes the return value of the yield that paused it. Going the other way, the values you hand to yield come back as the extra results of resume (after the leading true success flag), and a final return is delivered just like one last yield. This flow is what powers generators and pipelines.

Your turn. Drive a simple coroutine through its single pause and confirm it ends up dead . Fill in the blanks marked ___ .

3. wrap, Generators & Producer-Consumer

coroutine.wrap turns a coroutine into a plain function : each call resumes it and returns the yielded values directly, with no true flag to unpack (errors are re-raised instead of returned). That makes wrap perfect for generators — functions that produce a stream of values on demand — and for producer-consumer setups where one coroutine yields items and a loop consumes them. A wrap result even works directly as a for -loop iterator.

Now you try a generator. Finish the counter so each call hands out the next number starting from start . Fill in the blank:

No blanks this time — just a brief and an outline. You'll write a coroutine that does one step per "frame" and yields between them, exactly how scripted behaviours work in real game engines. Build it, run it, and check your output.

Practice quiz

Which function creates a new coroutine from a function?

  • coroutine.create
  • coroutine.new
  • coroutine.spawn
  • coroutine.make

Answer: coroutine.create. coroutine.create(fn) returns a coroutine object that is initially suspended.

How do you start or continue a suspended coroutine?

  • coroutine.start
  • coroutine.run
  • coroutine.resume
  • coroutine.go

Answer: coroutine.resume. coroutine.resume(co, ...) runs the coroutine until it yields, finishes, or errors.

What does coroutine.yield do?

  • Ends the coroutine permanently
  • Suspends the coroutine and returns control to the resumer
  • Creates a new thread
  • Pauses the whole program for a second

Answer: Suspends the coroutine and returns control to the resumer. yield suspends the coroutine, handing values and control back to whoever resumed it.

Are Lua coroutines preemptive OS threads?

  • Yes, they run in parallel on multiple cores
  • Only on Linux
  • Only in Lua 5.4
  • No, they are cooperative and run one at a time

Answer: No, they are cooperative and run one at a time. Coroutines are cooperative: exactly one runs at a time and it must yield to give up control.

What does the first return value of coroutine.resume indicate?

  • A boolean success flag
  • The coroutine's status string
  • The number of yields so far
  • The coroutine's id

Answer: A boolean success flag. resume returns true plus yielded values on success, or false plus an error message on failure.

Which function returns a coroutine's status such as 'suspended' or 'dead'?

  • coroutine.info
  • coroutine.status
  • coroutine.check
  • coroutine.state

Answer: coroutine.status. coroutine.status(co) reports suspended, running, normal, or dead.

What does coroutine.wrap return?

  • A status string
  • A coroutine object
  • A table of yielded values
  • A function you call to resume the coroutine

Answer: A function you call to resume the coroutine. wrap returns a callable; each call resumes the coroutine and returns its yielded values directly, raising errors instead of returning false.

How are values passed INTO a coroutine on the very first resume?

  • They become the return of yield
  • You cannot pass values in
  • As the arguments to the coroutine's body function
  • Through a global variable

Answer: As the arguments to the coroutine's body function. On the first resume, the extra arguments become the parameters of the function the coroutine wraps.

After the first resume, where do values passed to resume appear?

  • As new function arguments
  • As the return values of the yield that paused it
  • In coroutine.running
  • They are discarded

Answer: As the return values of the yield that paused it. On later resumes, the values you pass become the results returned by the yield that suspended the coroutine.

A common use of coroutines in game loops is to...

  • Write step-by-step behaviours that pause and resume each frame
  • Run code on multiple CPU cores at once
  • Replace the table data structure
  • Speed up math operations

Answer: Write step-by-step behaviours that pause and resume each frame. Coroutines let you write scripted, frame-by-frame behaviours that yield each frame and resume on the next.