Корутины

Знакомимся с корутинами — способом приостанавливать и возобновлять функции.

Корутина — это функция, которую можно поставить на паузу в любой момент и позже продолжить с того же места, сохранив все локальные переменные.

Обычная функция выполняется от начала до конца за один раз. Корутина может прерваться посередине, отдать управление, а потом продолжить. Это незаменимо для пошаговых сценариев в играх: диалоги, анимации, поведение врагов «по шагам».

Четыре главные операции

ФункцияЧто делает
coroutine.createсоздаёт корутину из функции
coroutine.resumeзапускает или продолжает её
coroutine.yieldставит корутину на паузу
coroutine.statusсообщает её состояние

Простой пример

local co = coroutine.create(function()
  print("шаг 1")
  coroutine.yield()      -- пауза
  print("шаг 2")
  coroutine.yield()      -- пауза
  print("шаг 3")
end)

coroutine.resume(co)   -- печатает шаг 1
coroutine.resume(co)   -- печатает шаг 2
coroutine.resume(co)   -- печатает шаг 3

Вывод:

шаг 1
шаг 2
шаг 3

Каждый resume продолжает корутину до следующего yield. Между вызовами корутина «спит», сохраняя своё место.

Передача значений

Через yield и resume можно передавать данные туда-обратно:

local gen = coroutine.create(function()
  for i = 1, 3 do
    coroutine.yield(i * 10)
  end
end)

print(coroutine.resume(gen))
print(coroutine.resume(gen))
print(coroutine.resume(gen))

Вывод:

true	10
true	20
true	30

resume возвращает true и значение, переданное в yield — получается генератор последовательности.

Состояния корутины

suspended  <-- на паузе, готова к resume
running    <-- сейчас выполняется
dead       <-- завершилась, resume больше не сработает

Как работает под капотом

Корутины Lua — это кооперативная многозадачность, а не настоящие потоки. Только одна корутина выполняется в каждый момент, и переключение происходит лишь добровольно — через yield. Поэтому здесь нет гонок данных и блокировок, как в обычной многопоточности: корутина сама решает, когда уступить. Именно так Roblox реализует wait() в скриптах.

Частые ошибки

  • Звать resume у мёртвой (dead) корутины — это вернёт false и сообщение об ошибке.
  • Путать корутины с настоящими потоками и ждать параллельного исполнения — они кооперативные.
  • Забыть, что yield можно вызывать только внутри корутины, а не в главном коде.

Итог

  • Корутина — функция, которую можно ставить на паузу через yield и продолжать через resume.
  • Основные функции: create, resume, yield, status.
  • Через yield/resume передаются значения — удобно для генераторов.
  • Это кооперативная многозадачность: одновременно работает лишь одна корутина, без гонок данных.
Проверьте себя
1. Что делает coroutine.yield внутри корутины?
AЗавершает корутину навсегда
BСтавит корутину на паузу, сохраняя её состояние для продолжения
CСоздаёт новую корутину
DУдваивает скорость
2. Корутины в Lua — это:
AНастоящие параллельные потоки с гонками данных
BКооперативная многозадачность: одновременно работает одна корутина
CСпособ ускорить циклы
DЗамена функции require
3. Что вернёт coroutine.resume для уже завершившейся (dead) корутины?
AПерезапустит её с начала
Btrue и последний результат
Cfalse и сообщение об ошибке
Dnil без сообщения