How do I fix this coroutine behavior

Recently, I have been having trouble with having a loop execute only while a function is still running.
Below is a sample of each script I used to attempt to execute it, however, when I “resume” (start in this case) the coroutine, its status still stays it is suspended.

-- This code block is a module script in ReplicatedStorage. For the sake of simplicity, we will refer to it as "ImportantModule"
local module = {
    testFunction = function(functionThing)
		local cool = coroutine.create(functionThing)
		coroutine.resume(cool)
		repeat

			print(coroutine.status(cool))
			wait()
		until coroutine.status(cool) == "dead"
        print("Inserted function has ended execution (experienced error or hit a return)")
	end,
}
return module

--This is a local script inside of a test gui calling this "testFunction"
local importantFunction = require(game.ReplicatedStorage:WaitForChild("ImportantModule"))

local functionToPassIn = function()
	wait(5)
	print("function has finished executing")
	return true
end
importantFunction.testFunction(functionToPassIn)

How would I go about fixing it and/or why does this fail?

I can’t confirm because I’m not on pc, but I think this is because when you do wait() inside of the coroutine, roblox handles this by taking the thread of the coroutine out of the scheduler (suspending the coroutine) until the wait ends, then when it resumes it won’t return control to the loop that checks until it hits another yield like wait() which suspends it again, so the loop would still only see that it’s suspended

and I think because of roblox’s implementation of multithreading only allows one thread to run at a time until it yields, you’ll never have the coroutine’s thread not suspended while the main thread is not suspended


an example of that last statement is the output of this

local output = ""

function saveToOutput(char)
   for i=1, 1e4, 1 do
      output = output .. char
   end
end

coroutine.wrap(saveToOutput)("A")
coroutine.wrap(saveToOutput)("B")

print(output)

this should result in always 10000 “A” in a row, then 10000 “B” in a row iirc
also, I’m not sure if 1e4 is right. you might need a larger value like 1e5 for the first coroutine to take a noticeable amount of time such that you’d think the second coroutine would run during the first one
however, the second does not because the first never gives up control with any yielding

an alternative to checking the state is having a boolean or bindable event

isRunning = true
func()
isRunning = false

you can share that boolean to different scopes by passing a table by reference, or by function calls from BindableFunctions


and for the bindable event, you can fire the event when the function ends

function makeAThread(func, ...)
   local bindableEvent = Instance.new("BindableEvent")
   coroutine.wrap(
      function(...)
         func(...)
         bindableEvent:Fire()
      end
   )(...)
   return bindableEvent
end

whatever code that runs makeAThread could wait until the bindable event fires, or connect to it and set a boolean when it fires

1 Like