Coroutine doesn't work

Hey, fellow devs.

I am currently working on a system involving coroutines, and I am having a weird issue regarding this.

So, you should probably take a quick look at the scripts before reading the rest of the text. Anyways, as you can see from the scripts, the script is basically running a quick for loop and waits a few seconds before the script then returns the bindable function. After that, it waits 2 seconds, and fires the bindable function again.

What I have noticed so far:
It dies (I believe) after the second time trying to continue it. (See comment).
I am not sure why this error occurs, I would love some help.

I recommend you, if it’s not too much to ask for, to test it out for yourselves in Roblox Studio.

Remember, it gets fired with a bindable function.

Server script 1 (the one firing):

bindableFunction = game.Workspace.Function

bindableFunctionVariable = bindableFunction:Invoke("randomVariable")

wait(2)

bindableFunctionVariable = bindableFunction:Invoke("randomVariable")

wait(2)

bindableFunctionVariable = bindableFunction:Invoke("randomVariable")

Server script 2 (the one receiving, and where the coroutine is):

bindableFunction = game.Workspace.Function

coroutineLoop = coroutine.create(function(someVariable)

	print(someVariable)

	for i = 1,10 do
	wait(0.1)
	end

	print("Yielding")
	coroutine.yield()

end)

function bindableFunction.OnInvoke(variable)

	print(coroutine.status(coroutineLoop))
	coroutine.resume(coroutineLoop, variable) --the second time resuming the coroutine, it seems to kill it. 
	print(coroutine.status(coroutineLoop))

	wait(1)
	print("SomethingRandom")
	wait(2)

	return

end

image

I would greatly appreciate any help, as this is really a big disturbance to my project. Thanks a lot! Really looking forward to help :slight_smile:

What Output are you getting when you run it?

suspended
randomVariable
suspended
SomethingRandom
Yielding
suspended
dead
SomethingRandom
dead
dead

After the coroutine prints “Yielding”, it yields once, and after that, it does nothing, and after that, it is dead, because there is nothing else to run.

One thing you should definitely keep in mind when dealing with coroutines in this manner, is that yielding functions… you know… yield. wait() will actually call coroutine.yield (after telling the thread scheduler to resume this thread later). This means the second print(coroutine.status(coroutineLoop)) in bindableFunction.OnInvoke(variable) will run immediately after the first wait(0.1).

It’s also possible to make a thread wait (with wait()) and resume it mid-wait, which will make wait() return early, return whatever you resumed the thread with instead of the time taken (I think. I haven’t tried it) and the thread will be resumed again by the thread scheduler, most likely when it’s already dead (which makes it error) or when it really shouldn’t be resumed.

Here is the code with lines annotatated:

--[[A1]]bindableFunction = game.Workspace.Function
--[[A2]]bindableFunctionVariable = bindableFunction:Invoke("randomVariable")
--[[A3]]wait(2)
--[[A4]]bindableFunctionVariable = bindableFunction:Invoke("randomVariable")
--[[A5]]wait(2)
--[[A6]]bindableFunctionVariable = bindableFunction:Invoke("randomVariable")

--[[B1]]coroutineLoop = coroutine.create(function(someVariable)
--[[B2]]	print(someVariable)
--[[B3]]	for i = 1,10 do
--[[B4]]	wait(0.1)
--[[B5]]	end
--[[B6]]	print("Yielding")
--[[B7]]	coroutine.yield()
--[[B8]]end)

--[[C1]]function bindableFunction.OnInvoke(variable)
--[[C2]]	print(coroutine.status(coroutineLoop))
--[[C3]]	coroutine.resume(coroutineLoop, variable) --the second time resuming the coroutine, it seems to kill it. 
--[[C4]]	print(coroutine.status(coroutineLoop))
--[[C5]]	wait(1)
--[[C6]]	print("SomethingRandom")
--[[C7]]	wait(2)
--[[C8]]	return

And here is the exact path I think the code takes:

--[[A1]]bindableFunction = game.Workspace.Function
--[[A2]]bindableFunctionVariable = bindableFunction:Invoke("randomVariable")
--[[C2]]	print(coroutine.status(coroutineLoop))
--[[C3]]	coroutine.resume(coroutineLoop, variable)
--[[B2]]	print(someVariable)
--[[B3]]	for i = 1,10 do
--[[B4]]	wait(0.1) -- coroutine. will yield immediately, going back to the main thread but being promised to be revived in 0.1 seconds
--[[C4]]	print(coroutine.status(coroutineLoop)) -- main thread. remember, this happened immediately
--[[C5]]	wait(1) -- main thread yields. now all threads are sleeping, waiting to be resumed
------------~0.1 seconds pass
--[[B4]]	wait(0.1) -- coroutine. resumed by thread scheduler 0.1 seconds after the for loop was first entered
------------~0.1 seconds pass
--[[B4]]	wait(0.1)
------------~0.1 seconds pass
--[[B4]]	wait(0.1)
------------~0.1 seconds pass
--[[B4]]	wait(0.1)
------------~0.1 seconds pass
--[[B4]]	wait(0.1)
------------~0.1 seconds pass
--[[B4]]	wait(0.1)
------------~0.1 seconds pass
--[[B4]]	wait(0.1)
------------~0.1 seconds pass
--[[B4]]	wait(0.1) -- this is nine wait()s, not ten - it's possible, but astronomically unlikely that all ten wait()s will run in the span of that wait(1), because I'm fairly sure the thread scheduler likes to run late, not early
------------indeterminate amount of seconds pass
--[[C6]]	print("SomethingRandom") -- main thread resumed by thread scheduler after that wait(1)
--[[C7]]	wait(2) -- main thread yields
------------indeterminate amount of seconds pass
--[[B4]]	wait(0.1) -- coroutine. resumed by thread scheduler
------------~0.1 seconds pass
--[[B6]]	print("Yielding")
--[[B7]]	coroutine.yield() -- coroutine yields to thread scheduler. thread scheduler does nothing
------------ a good bit less than 2 seconds pass
--[[C8]]	return -- main thread resumed after 2 seconds
--[[A3]]wait(2) -- main thread yields again. coroutine is still sleeping
------------~2 seconds pass
--[[A4]]bindableFunctionVariable = bindableFunction:Invoke("randomVariable")
--[[C2]]	print(coroutine.status(coroutineLoop))
--[[C3]]	coroutine.resume(coroutineLoop, variable)
--[[B8]]end) -- coroutine was resumed and reached the end. it is now dead.
--[[C4]]	print(coroutine.status(coroutineLoop)) -- prints dead, because coroutine is dead
--[[C5]]	wait(1)
------------~1 second passes
--[[C6]]	print("SomethingRandom")
--[[C7]]	wait(2)
------------~2 seconds pass
--[[C8]]	return
--[[A5]]wait(2)
------------~2 seconds pass
--[[A6]]bindableFunctionVariable = bindableFunction:Invoke("randomVariable")
--[[C2]]	print(coroutine.status(coroutineLoop)) -- prints dead
--[[C3]]	coroutine.resume(coroutineLoop, variable) -- should error???????
--[[C4]]	print(coroutine.status(coroutineLoop)) -- prints dead
--[[C5]]	wait(1)
--[[C6]]	print("SomethingRandom") -- you said this doesn't print, so the above resume really did error...
--[[C7]]	wait(2)
--[[C8]]	return
------------THE END

Fun coroutine experiment: run this in the command bar:

for _ in wait do end

If you understand why it prints TWO errors and why the second one doesn’t appear immediately, then you’re on your way to becoming a coroutine master.

Wow, thank you for taking your time to write such an extensive and informative text.

I did understand your post somewhat; So when yielding a coroutine and then resuming it, the coroutine will start where the yield() function is (right?) and thats why it dies, because the program sees that the coroutine doesn’t do anything.

I hope I’m right, if not please tell me :slight_smile:

But, what would a good solution be to this problem? I’m still a little confused. Thanks, and looking forward to your answer!

Edit: also, if you didn’t know, wrap the coroutine.resume() inside a assert() to get errors!