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.
deadThree functions from Lua's coroutine library do the work:
coroutine.create(emmasDay)makes a coroutine from the functionemmasDay. It doesn't run any of it yet. You pass the function itself, without parentheses.coroutine.resume(day)runs the coroutine until it reachescoroutine.yield()or the end of the function. The firstresumestarts at the top of the function; every next one continues right after theyieldwhere the coroutine stopped.coroutine.yield(), inside the function, pauses the coroutine, and the program goes on after theresumethat 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 insidecoroutine.create(emmasDay()). Both runemmasDayas an ordinary function, not as a coroutine, so itsyieldhas nothing to pause. The program printsEmma feeds the hens.and stops with the errorattempt to yield from outside a coroutine. This message has no line number; you'll find the line in thestack 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 getattempt to call a thread value (local 'day'). A coroutine runs only throughcoroutine.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: deadStart programming for free
1/6