Basics of Coroutines in Lua

Dive into the world of coroutines in Roblox with this guide.

by Vor_Der

Author Avatar

What are coroutines?

Coroutines in Lua are a way of organizing and executing code in a different sequence than the main program. They allow you to create separate "threads" of code that can be paused and resumed at specific points, which can be useful for tasks that take a long time to complete, such as network requests or complex calculations. Coroutines do not have their own independent execution context and do not run concurrently with the main program in the way that real threads do.

How to use coroutines in Roblox

The coroutine.create function is used to create a new coroutine in Lua. It takes a single argument, which is a function that defines the code to be executed in the coroutine. This function is often referred to as the "coroutine body".

Here's an example of how to use coroutine.create to create a coroutine:

-- Define a function that will be used to create the coroutine
local function task()
  -- Code to be executed in the coroutine goes here!
  print("Hello from the coroutine!")
end

-- Create a coroutine using the created "task" function
local taskCo = coroutine.create(task)

In this example, we are creating a new coroutine that simply prints a message when it is executed.

It's important to note that coroutine.create does not start the execution of the coroutine; it simply creates the coroutine and returns a reference to it. To start or continue the execution of the coroutine, you'll need to use the coroutine.resume function.

coroutine.resume()

This will start the execution of the coroutine, and the message "Hello from the coroutine!" will be printed to the output console.

I hope this explanation makes it clear and easy to understand how to use the coroutine.create function. It's just an introduction to the basics, so continue reading to get a more detailed understanding of different types of coroutines.


Using the 'coroutine.resume' Function

The coroutine.resume function is used to start or continue the execution of a coroutine in Lua. It takes a single argument, which is a reference to the coroutine, to be resumed. If the coroutine has not yet been started, coroutine.resume will start it. If the coroutine has already been started but is currently suspended, coroutine.resume will continue its execution from where it left off.

This is an example of coroutine.resume:

-- Define a function that will be used to create the coroutine
local function task()
  -- Code to be executed in the coroutine goes here!
  print("Hello from the coroutine!")
end

-- Create a coroutine using the created "task" function
local taskCo = coroutine.create(task)

coroutine.resume(taskCo) -- Prints "Hello from the coroutine!"

Understanding and Using the 'coroutine.yield' Function

The coroutine.yield function is used to pause the execution of a coroutine and return control to the main program. It can be used at any point during the execution of a coroutine, and the coroutine can be resumed later using the coroutine.resume function.

Additionally, It is not allowed to use the coroutine.yield function inside metamethods or iterators. With the exception of pcall and xpcall.

Metamethods are functions that define how tables in Lua behave in certain situations. They allow you to customize the behavior of tables by defining how they should respond to certain operations, such as addition, comparison, and indexing.

An iterator is a function that allows you to iterate over the elements of a collection, such as the elements of a table or the characters of a string. Iterators are typically implemented as closures, which are functions that access one or more local variables from their enclosing function. The variables keep their values across successive calls to the closure, allowing the closure to remember where it is along a traversal.

This is with exception of pcall and xpcall. These functions are used to execute a function in a protected environment, which allows the coroutine.yield function to be used safely.

This is an example of coroutine.yield:

local function task()
  print("Hello from the coroutine!")
  coroutine.yield()  -- Pause the coroutine and allow other tasks to run
  print("Coroutine resumed")
end

-- Create a coroutine
local taskCo = coroutine.create(task)

-- Start the coroutine
coroutine.resume(taskCo)  -- Prints "Hello from the coroutine!"

-- Resume the coroutine 
coroutine.resume(taskCo)  -- Prints "Coroutine resumed"


Using the 'coroutine.status' Function to Check the Status of a Coroutine

The coroutine.status function is used to check the status of a coroutine. It takes a single argument, which is a reference to the coroutine, and returns a string indicating the current status of the coroutine. The possible status values are:

This is an example of coroutine.status:

local function task()
  print("Hello from the coroutine!")
  coroutine.yield()  -- Pause the coroutine and allow other tasks to run
  print("Coroutine resumed")
end
 
local taskCo = coroutine.create(task)

-- Check the status of the coroutine
print(coroutine.status(taskCo))  -- Prints "suspended"

-- Start the coroutine
coroutine.resume(taskCo)  -- Prints "Hello from the coroutine!"

-- Check the status of the coroutine
print(coroutine.status(taskCo))  -- Prints "suspended"

-- Resume the coroutine
coroutine.resume(taskCo)  -- Prints "Coroutine resumed"

-- Check the status of the coroutine
print(coroutine.status(taskCo))  -- Prints "dead"



Checking if a Function Can Be Paused with 'coroutine.yield' Using 'coroutine.isyieldable'

The coroutine.isyieldable function is used to check whether the current function can be paused using coroutine.yield. It returns a boolean value indicating whether the current function is a coroutine or a thread. The coroutine.isyieldable function should be called from within a coroutine, not from the main program. If it is called from the main program, it will always return false because the main program is not a coroutine or a thread.

This is an example of coroutine.isyieldable:

local function task()
  print("Task started")
  coroutine.yield()
  print("Task resumed")
end

local taskCo = coroutine.create(task)

-- Check if the coroutine is yieldable
local yieldable = coroutine.isyieldable(taskCo)
print(yieldable)  --> true

-- Check if the main program is yieldable (not allowed)
yieldable = coroutine.isyieldable()
print(yieldable)  --> false
 

Obtaining a Reference to the Currently Running Coroutine with 'coroutine.running'

The coroutine.running function is used to get a reference to the currently running coroutine. If called from within a coroutine, it returns a reference to the coroutine. If called from the main program, it returns nil.

This is an example of coroutine.running:

local function task()
  -- Print the reference to the coroutine
  print(coroutine.running())
end

local taskCo = coroutine.create(task)

-- Check the reference to the current coroutine (main program)
print(coroutine.running())  -- Prints "nil"

-- Start the coroutine
coroutine.resume(taskCo)  -- Prints the reference to the coroutine



Conveniently Using Coroutines with 'coroutine.wrap'

Coroutine.wrap is a way to use a coroutine more conveniently. When you wrap a coroutine in a wrapper function, you get back a function that you can call multiple times. Each time the function is called, it will resume the coroutine and return the values that were either returned by the coroutine or passed to coroutine.yield. This allows you to do things like produce values from inputs or perform work on a subroutine without having to worry about manually resuming the coroutine each time.

This is an example of coroutine.wrap:

local function task()
  print("Hello from the coroutine!")
  return "A message from the coroutine"  -- Return a value to the wrapper function
end

-- Create a wrapper function for the coroutine using the created task function
local myCoroutine = coroutine.wrap(task)

-- Start the execution of the coroutine
local result = myCoroutine()  -- Will print "Hello from the coroutine!" and returns "A message from the coroutine"
print(result)  -- Prints "Hello from the coroutine!" and returns "A message from the coroutine"

Halting the Execution of a Coroutine with 'coroutine.close'

The coroutine.close function is used to close a coroutine and put it into a dead state. This means that the coroutine can no longer be resumed, and any resources associated with it will be released. If you try to close a coroutine that is currently running, the function will return an error. If the coroutine is in an error state, the function will return false and the error message. Otherwise, it will return true to indicate that the coroutine was successfully closed.

This is the last example, I've added some additional comments to explain each change in the code:

-- Define a function that will be used to create the coroutine
local function task()
  print("Hello from the coroutine!")
  coroutine.yield()  -- Pause the coroutine and allow other tasks to run
  print("Coroutine resumed")
end

-- Create a coroutine 
local taskCo = coroutine.create(task)

-- Start the coroutine
coroutine.resume(taskCo)  -- Prints "Hello from the coroutine!"

-- Close the coroutine
local success = coroutine.close(taskCo)
print(success)  --> true

-- Attempting to resume a closed coroutine will result in an error
success, result = coroutine.resume(taskCo)
print(success, result)  --> false, cannot resume dead coroutine


Here are a few things to keep in mind when using coroutines in Lua:

When a coroutine is created using the coroutine.create function, it is given its own stack and a separate set of local variables. However, it does not have its own independent execution context, and it does not run concurrently with the main program.

Instead, the execution of the coroutine is controlled by the main program using the coroutine.resume function. When the coroutine.resume function is called, the coroutine is started or resumed from the point where it was previously paused, and it runs until it reaches a yield or return statement. At this point, control is returned to the main program, and other tasks can be run.

I hope this explanation helps to clarify the concept of coroutines in Lua!


I hope this tutorial makes it clear and easy to understand how to use coroutines. If you have any questions or find anything difficult to understand, or incorrect, please leave a comment.

image|100x100, 10%

View in-game to comment, award, and more!