Method for yielding a script

Right now if you call coroutine.yield in a scripts body the thread scheduler will automatically resume the script. It would be really useful in certain situations to have the thread actually yield until we decide to resume it ourselves.

A use case for this is in custom events. Currently you have to do something like (waitForEvent)

table.insert(threads, coroutine.running())
local output repeat
	output = {coroutine.yield()}
	local valid = table.remove(output, 1)
until type(valid) ~= "number" -- The scheduler returns a number from yield
coroutine.yield() -- Ensures the scheduler regains control of the thread
return unpack(output)

When really it should be as simple as

table.insert(threads, coroutine.running())
return coroutine.yield()

Bindables don’t solve this, but moreover there are other uses beyond event handling.

2 Likes

You can do this with events

e = Instance.new'BindableEvent'
print'a'
e.Event:wait()
print'c'

somewhere else:

print'b'
e:Fire()
print'd'

-> a b c d

3 Likes

This isn’t a valid solution in the case of metatables.

What do you mean?

Source: http://wiki.roblox.com/index.php?title=API:Class/BindableEvent

So if I want to return a value that does not meet that criteria I need to use custom threading.

local class = {}
class.__index = class
class.WillBeLost = "Hi"

local obj = setmetatable({
  WillNotBeLost = "Hello"
}, class)

e:Fire(obj)
local obj = e.Event:Wait()
print(obj.WillBeLost) >> nil

I am creating custom objects and firing my own events when they’ve been created. In the case of wait the objects would lose information and their metatables.

You don’t send any arguments to the event. You just use it for yielding.

It would be nice to “unregister a thread as a roblox thread” (me and a lot of people call Lua threads “roblox threads” when they’re in the sceduler, not sure if there’s an official name for it), but for your issue, a BindableEvent would work fine:


local function CreateEvent()
	local be = Instance.new("BindableEvent")
	local event,cache = {},{}
	function event:Connect(f)
		return be.Event:connect(function(key)
			local t = cache[key]
			f(unpack(t,1,t.n))
		end)
	end event.connect = event.Connect
	function event:Wait()
		local t = cache[be.Event:wait()]
		return unpack(t,1,t.n)
	end event.wait = event.Wait
	function event:Fire(...)
		local key = tick()
		cache[key] = {n=select("#",...),...}
		be:Fire(key) cache[key] = nil
	end return event
end

local ev = CreateEvent()

spawn(function()
	local data = {Banana=123}
	local test = setmetatable({},{__index=data})
	ev:Fire(data)
end)

ev:connect(function(uhu)
	print("Connect:",uhu.Banana)
end)
print("Wait:",ev:wait().Banana)
--> Wait: 123
--> Connect: 123

Lots of people have rewritten custom events that are basically BindableEvent wrappers so they can pass arguments directly. While that would solve your problem, being able to coroutine.yield() “for real” in roblox threads would be nice. Could be something like coroutine.ryield() or whatever. Maybe just have coroutine.setrobloxthread(false) remove the current (or passed-as-argument?) thread from the scheduler.

TL;DR: Being able to “unschedule” threads isn’t necessary for your problem, but nice to have.

While the wiki doesn’t have a lot of information about roblox threads, it has a bit:
http://wiki.roblox.com/index.php?title=Beginners_Guide_to_Coroutines#Interaction_with_the_thread_scheduler
http://wiki.roblox.com/index.php?title=Thread_scheduler
Some of the information is outdated (or slightly wrong), but still a good read.

2 Likes