What are coroutines and those function on its library

i found SOME information about coroutines and what theyre useful for
its like a function that doenst yield other stuff from the script or something
but for some i have no idea

the ones i couldnt understand were wrap, yield, isyieldable, running
also unrelated but task.desynchronize, task.synchronize, pararell luau,

tried looking in the documentation but my brain wanst braining and forgot the concept of comprehension and didnt understand any of it

1 Like

coroutine

A coroutine is used to perform multiple tasks at the same time from within the same script. Such tasks might include producing values from inputs or performing work on a subroutine when solving a larger problem. A task doesn’t even need to have a defined ending point, but it does need to define particular times at which it yields (pause) to let other things be worked on.

Using Coroutines

A new coroutine can be created by providing a function to coroutine.create(). Once created, a coroutine doesn’t begin running until the first call to coroutine.resume() which passes the arguments to the function. This call returns when the function either halts or calls coroutine.yield() and, when this happens, coroutine.resume() returns either the values returned by the function, the values sent to coroutine.yield(), or an error message. If it does error, the second return value is the thrown error.

isyieldable

boolean

Returns true if the coroutine this function is called within can safely yield. Yielding a coroutine inside metamethods or C functions is prohibited, with the exception of pcall and xpcall.

3 Likes

This is the way I understand it..

task.spawn is for a solid, never-yielding routine.. it just runs asynchronously and keeps going without manual pausing.

Coroutines are for functions that can stop (yield) and start again, letting you pause a routine without freezing the rest of the script.

The other coroutine functions (wrap, isyieldable, running, etc.) are just variations or tools to manage that pausing/resuming behavior more flexibly.

You can still mimic coroutines with tasks though.

Ex:

local thread: thread
local conditions = {}

thread = task.spawn(function()
	print("this is my print.")
	print("lets wait for a condition to be met!")
	repeat task.wait() until conditions[1] == 1
	print("condition met!")
end)

task.delay(2, function()
	conditions[1] = 1
end)

1 Like

But polling, e.g: repeat task.wait() until condition should always be avoided.

1 Like

A coroutine is basically a separate thread, and the coroutine library provides useful wrappers to interact with these threads.

A thread is basically an object that allows the running of code. On any given Luau VM (virtual machine), there is one main thread. In Roblox, this is tied to the ScriptContext service. Any other threads are subthreads of this main thread.

So how is this relevant? Well, there’s an important distinction between threads and the coroutine library. Coroutines are threads. Your scripts are threads. The coroutine library you see in Luau just provides wrappers around the Luau C API to allow you to interact with these threads.

I’ll break them down one by one:

coroutine.create

  • This creates a new thread from the main thread (wraps lua_newthread) and pushes the given function to the top of it’s stack, ready for resumption. The thread is left in the yield state, meaning it is not running currently but can be “resumed” to run. You could say you’ve loaded everything you need for the thread, but it’s paused.

coroutine.resume

  • This resumes, or “unpauses” a yielded thread (wraps lua_resume). It’ll pass the extra arguments given yto coroutine.resume as return values of coroutine.yield from within the thread’s work.

coroutine.yield

  • This yields (“pauses”) a thread (wraps lua_yield). Any arguments you pass to coroutine.yield will be sent back to the code which resumed the thread before.

coroutine.isyieldable

  • You’re basically asking the Luau VM, “hey, is this thread safe to yield?” (wraps lua_isyieldable). It’s basically the Luau VM telling you whether or not it’s safe to call coroutine.yield on the thread.

coroutine.running

  • All this does is return the currently running thread.

coroutine.wrap

  • This will construct a new thread with the given function, then return a function that when called will resume the new thread, with the given arguments.
local f = coroutine.wrap(function()
    print("hi from another thread")
end)

f() --resumes the new thread

ok, but what about the task library?

  • The task library and the coroutine library are both built around threads, just the behaviour of the task library is slightly different. For example, task.spawn will create a thread but will then immediately resume it, as opposed to coroutine.create which will leave it yielded. The task library is built more around the Roblox task scheduler - providing functions like delay and defer to control when threads are executed more seamlessly. Note that functions like coroutine.resume will yield your current thread until the thread you resumed yields, but functions like task.spawn will just resume the other thread alongside your current one.

and yes - this means you can mix and match coroutine and task functions!

Example: coroutine

local function myWork()
	local thisThread = coroutine.running() --the thread this function is running on
	print(coroutine.status(thisThread)) --> "running"
	
	local arg1, arg2 = coroutine.yield(3, "hi") --pause this thread, return 3 and "hi" to
	--the thread which resumed this one
	
	--when this thread is next resumed, arg1 and arg2 will be whatever else was passed
	--to coroutine.resume
	print(arg1, arg2) --> 5 "hi" in this case
end


--create a new thread in yielded status, and push myWork to the top
--of it's stack
local newThread = coroutine.create(myWork)

--run myWork on the new thread. coroutine.resume will
--yield this thread until that thread yields or finishes.
local success, result1, result2 = coroutine.resume(newThread)
print(success, result1, result2) --> true 3 "hi"

--let's resume the yielded thread with args
coroutine.resume(newThread, 5, "hi")

Example: task

local function myWork()
	local thisThread = coroutine.running()
	print(coroutine.status(thisThread)) --> "running"
	
	print("you can put work to run alongside your other thread in here.")
end

local thread = task.spawn(myWork)

Example: Mixing the two

--you CAN use task.defer and pass arguments
--into that directly, which will pass them to
--myWork as parameters. This is for demo purposes.

local function myWork()
	local arg1, arg2 = coroutine.yield() --task.spawn immediately resumes the thread so we'll yield it
	print(arg1, arg2) --> "hi" 3
	
	print("hello from this thread")
	local newArg = coroutine.yield(3)
	
	print(newArg) --> 5
end

--let's create the thread with task.spawn
local thread = task.spawn(myWork)

print(coroutine.status(thread)) --> "suspended"

--since we immediately yielded it, we can give it
--arguments through coroutine.resume
local success, returnVal = coroutine.resume(thread, "hi", 3)
print(returnVal) --> 3

--the other thread yielded again. Let's resume it with
--task.spawn, because you can resume a thread as well
--as a function through task.spawn
task.spawn(thread, 5)

I hope this helps, if you have any questions please ask.

2 Likes

You can yield within task.spawn

Please prefer explaining relative to Luau code, nobody here is using the C API. There’s no point giving your explanations with reference to the stack because that’s not relevant and is just an implementation detail.

2 Likes

Don’t do that ever again please;
This is just suffering and deoptimization for the sake of suffering and deoptimization.

I guess I got a little carried away with coroutine.resume sorry, ive edited the response for that but i still feel it needed to mention the stack in the context of threads because it’s a fundamental part of how they work and differ from the main thread, and parallel luau. Thanks for the feedback (ive also reviewed the rest of my response)

3 Likes

Why? Just wondering

It constantly checks for changes even if no change has occurred and this can easily consume resources. Secondly, there’s not much scalability with that type of design,

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.