How and when to use coroutine.yield

The wording for the topic will mention how you shouldn’t use :Wait that is because I was using that as the title name for a while.

This subject is way more complex than just stopping to use :Wait, you SHOULD use :Wait for most cases.


This is in case you’re specifically using BindableEvents or some Signal API to wait for somrthing to happen in another coroutine.

  • You can get a coroutine instance from any script, anywhere, anytime using coroutine.running! Every script runs in a separate coroutine.
  • Roblox threads can behave differently, they are still get-able through coroutine.running, but they behave a little difference depending on what you do on them, in this case using coroutine.yield / resume can have a slightly different effect which you can read about here.

An example might be having a timer system using a RunService event and you wanna resume the code once the timer finishes.

local RunService = game:GetService('RunService')
local timeLeft = 10

local Event = Instance.new('BindableEvent')

local connection;
connection = RunService.Heartbeat:Connect(function(delta)
    timeLeft -= delta;
    if timeLeft <= 0 then
        connection:Disconnect();
        Event:Fire();
    end
end)

Event.Event:Wait()
Event:Destroy()

And the problem here, is memory usage. Lua memory management sometimes is weird in my experience, and overall if you can use less memory in a still reasonable matter, then why not?

If you’re using a Signal API or a BindableEvent, you have more garbage data that you might think.

In a custom Signal API you’re gonna be holding references to a lot of tables sometimes, and for BindableEvents, you have properties like Name, a bunch of sub events like .Changed.

And anyhow, custom Signal APIs, ones that don’t use BindableEvents, are gonna be using the ‘tip’ internally which I show here, except that these Signal APIs will use way more memory than just using this purely.

So what are you supposed to do?

Use coroutine.yield and coroutine.resume.

It’s super simple, here’s an example of the previous script except with these functions.

local RunService = game:GetService('RunService')
local timeLeft = 10

local thread = coroutine.running() --// Gets the 'coroutine' instance were in.

local connection;
connection = RunService.Heartbeat:Connect(function(delta)
    --\\ Connections are ran in another coroutine instance.

    timeLeft -= delta;
    if timeLeft <= 0 then
        connection:Disconnect();
        coroutine.resume(thread);
    end
end)

coroutine.yield() --\\ 'Pauses' the current 'coroutine' instance.
3 Likes

Extra info

You can use coroutine.resume with extra arguments! These extra arguments are ‘passed-through’ coroutine.yield.

A bit confusing, but let me show you.

local RunService = game:GetService('RunService')
local timeLeft = 10

local thread = coroutine.running() --// Gets the 'coroutine' instance were in.

local connection;
connection = RunService.Heartbeat:Connect(function(delta)
    --\\ Connections are ran in another coroutine instance.

    timeLeft -= delta;
    if timeLeft <= 0 then
        connection:Disconnect();
        coroutine.resume(thread, timeLeft - 10);
    end
end)

local yieldedFor = coroutine.yield() --\\ 'Pauses' the current 'coroutine' instance.

--\\ coroutine.yield will return the extra arguments you passed through coroutine.resume.

So it’s basically the same idea as getting the data that the signal was fired with returned by :Wait().

2 Likes

This seems to not be the same anymore, but I can’t find anything saying it isn’t either.
Disregard the post below for now.

Old post

So I got some extra info, on which you might wanna use a new Signal.

This is only valid whoever, if you’re using a BindableEvent solution, otherwise, the above still applies.

What can happen if you use coroutine.yield is that it can yield for an extra amount of time inside Roblox threads, roblox threads are usually threads that were created either from a script, or spawn and it’s variants (I think), and also inside connections.

This extra amount of time is basically a wait(), maybe a Heartbeat:Wait() but not sure.

It doesn’t poll, don’t worry, however there is a wait() in there after you resume a thread, this is actually a workaround Roblox added, before you could not use these functions on Roblox threads.

In that case, using :Wait() as long as it’s derived from a BindableEvent is just fine and doesn’t have that ‘limitation’.

This is not a problem if you’re doing that inside a coroutine that YOU made, only the ones that Roblox automatically creates, etc.

If you absolutely need to have a instant resume and you’re only doing that here and there, then sure, use :Wait() with no issue.

Like I said, if the Signal API you’re using is not using :Wait() from a Signal itself and instead using coroutine.resume / yield, then there’s no difference, your Signal API should use :Wait() instead of coroutine.resume / yield if it’s using BindableEvents.

1 Like

Fun fact, using coroutines uses more memory than not using them.
It needs to store what the coroutine should be running, its state, and all the functions associated with it.

The only main use for them is multithreaded applications.

3 Likes

It doesn’t? Do you mean the coroutine library?

I’m really confused by what you mean.

1 Like

Ok, I think I understand what you mean, but it doesn’t seem like it would change anything.

The thread should at least in my mind, already have their data in there, you’re just getting it, the problem I could think of is garbage collection if you keep a variable with that data, then that’s surely valid, but what you said doesn’t make sense in my mind at least, I wanna understand what you mean here, but I don’t know.

1 Like

Hi Ben.

How are you?

2 Likes

As you know, using a function in lua needs to store that functions code in memory. Now, if you’re only using that one function then the source code is the only data stored in memory. Say you start using coroutines to handle your function. The coroutine library has multiple submodules, functions, attributes, etc. that it uses to function. The thing is, obviously, the more of these the more memory lua is going to use since it needs to store said data/attributes. Now, take that and multiply it by EVERY function in your script that uses coroutines. You start gaining memory usage rather fast (which is weird since it would seem like ROBLOX optimized this, but they didnt). So in short, using coroutines uses more memory (sorry if this doesn’t relate to your tutorial, I just saw the claim and felt like pointing this out.).

@Crcoli737 I’m quite alright. Still not satisfied with your fame levels yet. Wheres the big multi-billion dollar company?

3 Likes

I’m working on a roblox game with 700k visits.

1 Like

Working on and made are two different things.
Also- why are you trying to hit it off with a childrens game?

1 Like

They asked me to join their team so I mean. Why not?

I’m making lol.

1 Like

Please can people not treat Roblox as a kids game. It’s a game for all audiences.

3 Likes

Fair point. Many developers make thousands of dollars off of Roblox, and it’s fair to say most “kids” games don’t pay their players/developers that kind of money.

1 Like