Pausing a Function

Welcome to the lesson on coroutines, the last big piece of Lua. Many beginners have never met anything like them, so let's start with an idea, not with code.

Every function you've written so far runs from start to end in one go: when you call it, the rest of the program waits until it's done. Now imagine Emma telling you about her day, one part at a time. In the morning, she says: “I've fed the hens.” Then she goes off to work. When you meet her at noon, she doesn't start over; she goes on: “I've watered the carrots.” She remembers where she stopped.

A coroutine is a function that can do the same: stop in the middle, remember where it was (and the values of its local variables), and later continue from that very spot. The name means “routines that work together”: “routine” is an old word for a function, and a coroutine takes turns with the rest of the program. Here's Emma's day as a coroutine:

-- Emma's day, step by step
local function emmasDay()
    print("Emma feeds the hens.")
    coroutine.yield()
    print("Emma waters the carrots.")
    coroutine.yield()
    print("Emma sells eggs at the market.")
end

local day = coroutine.create(emmasDay)
print(coroutine.status(day))
coroutine.resume(day)
print("(The sun climbs higher.)")
coroutine.resume(day)
print(coroutine.status(day))
coroutine.resume(day)
print(coroutine.status(day))

This prints:

suspended
Emma feeds the hens.
(The sun climbs higher.)
Emma waters the carrots.
suspended
Emma sells eggs at the market.
dead

Three functions from Lua's coroutine library do the work:

  • coroutine.create(emmasDay) makes a coroutine from the function emmasDay. It doesn't run any of it yet. You pass the function itself, without parentheses.
  • coroutine.resume(day) runs the coroutine until it reaches coroutine.yield() or the end of the function. The first resume starts at the top of the function; every next one continues right after the yield where the coroutine stopped.
  • coroutine.yield(), inside the function, pauses the coroutine, and the program goes on after the resume that started it. (“Yield” means to give way, like a car at a crossing.)

coroutine.status(day) tells you how the coroutine is doing: suspended (paused: not started yet, or waiting at a yield), running (its code is running right now, so only the coroutine itself could ever see this) or dead (it has reached the end of its function and can't continue).

Why is this useful? Games are full of things that take a while: Hugo the trader traveling from town to town, a door slowly opening, Emma walking across the field. A game draws a new picture (a frame) many times a second, or, in a turn-based game, gives each character a turn, and all those things must move on one small step at a time. With a coroutine, you write such a thing as one ordinary function, from start to end, with coroutine.yield() wherever it should wait for the next frame or turn. You'll write such scripts later in this lesson.

Typical mistakes:

  • Calling the function yourself, emmasDay(), or with parentheses inside coroutine.create(emmasDay()). Both run emmasDay as an ordinary function, not as a coroutine, so its yield has nothing to pause. The program prints Emma feeds the hens. and stops with the error attempt to yield from outside a coroutine. This message has no line number; you'll find the line in the stack traceback: below it.
  • Calling the coroutine like a function, day(). A coroutine isn't a function; it's a value of its own type, thread, so you get attempt to call a thread value (local 'day'). A coroutine runs only through coroutine.resume.

Instructions

Emma waters her field, but after the first plot, she wants a break. Turn waterField into a coroutine: put coroutine.yield() after watering plot 1 and after plot 2 (not after plot 3). Replace the call waterField() with local watering = coroutine.create(waterField), resume watering once, and then print Emma takes a break. The program prints:

Plot 1 watered
Emma takes a break.

After the break, print the coroutine's status after the text Status: . Then resume watering twice more and print its status again. The program prints:

Plot 1 watered
Emma takes a break.
Status: suspended
Plot 2 watered
Plot 3 watered
Status: dead

Start programming for free

By signing up, you agree to the Terms of Service and Privacy Policy.

Or sign up with:

1/6