0Pricing
Lua Academy · Урок

resume и yield

Обменивайтесь данными между вызывающим кодом и сопрограммой с помощью resume/yield.

«resume и yield» — бесплатный урок Lua Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Lua Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Lua Academy содержит 4 уроков всего.

Передача значений корутине

Аргументы, переданные в первый вызов coroutine.resume, становятся параметрами функции, лежащей в основе корутины. Аргументы, переданные в последующие вызовы возобновления, возвращаются вызовом coroutine.yield(), который приостановил корутину.

local co = coroutine.create(function(a, b)
  print("received:", a, b)        -- 10  20
  local c = coroutine.yield()     -- pauses
  print("after yield:", c)        -- 30
end)

coroutine.resume(co, 10, 20)  -- first call: passes a,b
coroutine.resume(co, 30)      -- subsequent: passed to yield

Значения из yield

Значения, переданные в coroutine.yield(val1, val2), становятся возвращаемыми значениями вызова coroutine.resume(), приостановившегося на yield. Таким образом, yield «возвращает» значения вызывающему коду.

local co = coroutine.create(function()
  coroutine.yield(1, "hello")
  coroutine.yield(2, "world")
  return 3, "done"
end)

local ok, a, b = coroutine.resume(co)
print(ok, a, b)   -- true  1  hello

ok, a, b = coroutine.resume(co)
print(ok, a, b)   -- true  2  world

ok, a, b = coroutine.resume(co)
print(ok, a, b)   -- true  3  done

Двунаправленный обмен данными

Корутины обеспечивают настоящий двунаправленный обмен данными: вызывающий код передаёт данные через resume, корутина обрабатывает их и возвращает результаты через yield. Каждый вызов resume/yield представляет собой синхронный обмен.

local co = coroutine.create(function(x)
  while true do
    x = coroutine.yield(x * x)  -- yield x^2, get next x
  end
end)

coroutine.resume(co)  -- start (x=nil initially)
local ok, sq
ok, sq = coroutine.resume(co, 3); print(sq)  -- 9
ok, sq = coroutine.resume(co, 5); print(sq)  -- 25
ok, sq = coroutine.resume(co, 7); print(sq)  -- 49

Производитель и потребитель

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

local function producer(items)
  return coroutine.create(function()
    for _, item in ipairs(items) do
      coroutine.yield(item)
    end
  end)
end

local pro = producer({"apple","banana","cherry"})

-- Consumer drives the loop
while true do
  local ok, item = coroutine.resume(pro)
  if not ok or item == nil then break end
  print("consumed:", item)
end

Конвейер с корутинами

Объединяйте несколько корутин в конвейер. Каждый этап считывает данные с предыдущего этапа и передаёт результаты следующему через yield. Последний этап запускает конвейер, возобновляя последнюю корутину, после чего сигнал проходит каскадом обратно по цепочке.

local function source(items)
  return coroutine.wrap(function()
    for _, v in ipairs(items) do coroutine.yield(v) end
  end)
end

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

local function map(iter, fn)
  return coroutine.wrap(function()
    for v in iter do coroutine.yield(fn(v)) end
  end)
end

local pipeline = map(filter(source({1,2,3,4,5,6}),
  function(n) return n%2==0 end),
  function(n) return n*n end)

for v in pipeline do io.write(v.." ") end
print()  -- 4 16 36

Корутины для имитации асинхронности

Без потоков OS корутины могут имитировать асинхронные операции: выполняйте yield во время «ожидания» и возобновляйте корутину, когда «результат» готов. Планировщик возобновляет корутину, передавая ей результат.

local scheduler = {}
local current = nil

local function await(key)
  coroutine.yield(key)  -- tell scheduler what we need
  return coroutine.yield()  -- wait for result
end

local co = coroutine.create(function()
  local data = await("fetch:users")
  print("Got users:", data)
end)

-- Start coroutine
local ok, want = coroutine.resume(co)
print("Coroutine wants:", want)  -- fetch:users
-- Simulate delivering the result:
coroutine.resume(co, {{name="Alice"},{name="Bob"}})

yield внутри функции

Вы можете выполнить yield внутри любой функции, вызванной корутиной, а не только внутри функции верхнего уровня. yield проходит через все вложенные вызовы обратно к resume. Это называется «приостановленным стеком вызовов».

local function yieldTwice(label)
  coroutine.yield(label .. ":1")
  coroutine.yield(label .. ":2")
end

local co = coroutine.create(function()
  yieldTwice("A")
  yieldTwice("B")
end)

for i = 1, 4 do
  local ok, v = coroutine.resume(co)
  print(v)
end
-- A:1, A:2, B:1, B:2

Корутины — не потоки

Корутины Lua — это не потоки OS. Одновременно выполняется только одна корутина; вытеснения и параллелизма нет. Все корутины используют один поток OS. Для настоящего параллелизма нужны несколько состояний Lua в отдельных потоках OS.

-- This is NOT parallel - interleaved execution
local function task(name, n)
  for i = 1, n do
    print(name, i)
    coroutine.yield()
  end
end

local t1 = coroutine.create(function() task("A", 3) end)
local t2 = coroutine.create(function() task("B", 3) end)

-- Must explicitly schedule:
for _ = 1, 3 do
  coroutine.resume(t1)
  coroutine.resume(t2)
end
-- A1, B1, A2, B2, A3, B3 (interleaved, NOT parallel)

Состояние после возобновления

Проверяйте состояние корутины, чтобы определить, нужно ли возобновлять её снова. Корутина, завершившая работу (вернувшаяся из своей функции), находится в состоянии «завершена» — попытка возобновить её возвращает false и ошибку. Всегда проверяйте состояние перед возобновлением в циклах.

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

while coroutine.status(co) ~= "dead" do
  local ok, val = coroutine.resume(co)
  if ok and val ~= nil then
    print("val:", val)
  end
end
-- val: 1, val: 2, val: 3

Несколько yield за одно возобновление

Один вызов coroutine.resume выполняет корутину до следующего yield или до её завершения. Нельзя заставить корутину выполнить yield несколько раз за одно возобновление: каждый yield приостанавливает её и ожидает следующего возобновления.

local co = coroutine.create(function()
  local x = coroutine.yield("first")
  print("got:", x)
  local y = coroutine.yield("second")
  print("got:", y)
end)

local _, v1 = coroutine.resume(co)         -- starts, yields "first"
print("yielded:", v1)                       -- first
local _, v2 = coroutine.resume(co, "a")   -- passes "a", yields "second"
print("yielded:", v2)                       -- second
coroutine.resume(co, "b")                   -- passes "b", coroutine ends

Быстрая проверка

Во что превращаются значения, переданные в coroutine.yield(v1, v2)?

Итоги: resume и yield

Краткое содержание:

  • Аргументы первого resume → параметры функции корутины
  • Аргументы последующих resume → значения, возвращённые предыдущим yield()
  • Аргументы yield → значения, возвращённые вызовом resume(), запустившим этот yield
  • Двунаправленный обмен: каждый resume/yield — это обмен данными
  • yield работает из любой вложенной функции
  • Корутины работают кооперативно, а не параллельно

Часто задаваемые вопросы

Урок «resume и yield» бесплатный?

Да — полный текст урока «resume и yield» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Lua Academy, подпишись на CoddyKit PRO. Курс Lua Academy содержит 4 уроков всего.

Чему я научусь в уроке «resume и yield»?

Обменивайтесь данными между вызывающим кодом и сопрограммой с помощью resume/yield. Ты практикуешь Lua Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Lua Academy?

Предыдущий опыт не требуется. Lua Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «resume и yield»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Lua Academy?

Да. Каждый урок Lua Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Создание сопрограмм
  2. resume и yield
  3. Состояние сопрограммы
  4. Шаблон «производитель—потребитель»
← Назад к Lua Academy