User:NecroBumpist/Tutorials/Coroutines

From Legacy Roblox Wiki
Jump to navigationJump to search

In this custom tutorial, I will demonstrate how to utilize coroutines, a very interesting aspect of Lua.


Description

Coroutines are very similar to subroutines (also known as functions), because they do exactly the same thing, just in a special manner.

What makes coroutines different is that unlike subroutines, coroutines can be executed partially, stopped, and then resumed from where they were at a different time.

With this, you can share execution time in Lua, just like your computer is doing right now.

Creating a Coroutine

Coroutines are actually 'threads' which encompass a normal Lua subroutine, just like Scripts do!

To create a coroutine, you have to use coroutine.create() like so:

local function subroutine()
    print("Inside a coroutine!");
end

local thread = coroutine.create(subroutine);        -- this turns subroutine() into a real coroutine

Starting a coroutine

Once you've created a coroutine, you must use coroutine.resume() to begin execution. You can also pass arguments to the coroutine this way. It should be kept in mind that a function can only be executed completely once. Once a function returns the coroutine cannot be started again. (The solution is to recreate the coroutine)

NOTE: coroutine.resume() is like pcall() in that it returns whether or not the function ran properly.

local function subroutine(val)
    local num;

    for i = 1, val do
        num = math.random();
    end

    return num;
end

local thread = coroutine.create(subroutine);
local Work, SecureNumber = coroutine.resume(thread, 500);
print(SecureNumber)    --> 0.99261452070681

Stopping coroutines mid-execution

Using a combination of coroutine.yield() and coroutine.resume() you can finally use coroutines in the way they were meant to be used.

Together, you will be able to stop a coroutine at a certain point, go do some other work, and then resume the coroutine.

local function subroutine()
    for i = 1, 3 do
        coroutine.yield("This coroutine stopped. `i` = " .. i);        -- this is where the coroutine stops, and will resume from
    end
end

local thread = coroutine.create(subroutine);

for i = 1, 3 do
    print(coroutine.resume(thread));
end

Output:
true	This coroutine stopped. `i` = 1
true	This coroutine stopped. `i` = 2
true	This coroutine stopped. `i` = 3

It is possible to pass values between a coroutine and its calling thread via coroutine.resume() and coroutine.yield().

local function subroutine(input)
    print("Coroutine: `input` = " .. input);
    local secondInput = coroutine.yield("Coroutine is working!");
    print("Coroutine: `secondInput` = " .. secondInput);
    return "Finished!";
end

local thread = coroutine.create(subroutine);
local midway = coroutine.resume(thread, "Sorcus is EPIC");
print("Main thread: `midway` = " .. midway);
local output = coroutine.resume(thread, "Oysi is EPIC as well");
print("Main thread: `output` = " .. output);

Output:
Coroutine: `input` = Sorcus is EPIC
Main thread: `midway` = Coroutine is working!
Coroutine: `secondInput` = Oysi is EPIC as well
Main thread: `output` = Finished!

Various other functionality

Now you know the basics of how to use coroutines, so practicing with them a little is advised, but there is still more to learn. There are several other secondary coroutine functions which can be quite useful. I will describe these briefly here, follow the links to learn more.

coroutine.running()

coroutine.running() returns the thread for the currently executing coroutine, or nil if called from the main thread.

coroutine.status(co)

coroutine.status() returns a string describing the state of the current thread, whether it is capable of being ran, paused, or dead.

coroutine.wrap(f)

coroutine.wrap() combines the abilites of coroutine.resume() and coroutine.create(), in that it returns a function that will resume the function it is provided.