Consider Quenty’s fastSpawn implementation:
function fastSpawn(func, ...)
assert(type(func) == "function")
local args = {...}
local count = select("#", ...)
local bindable = Instance.new("BindableEvent")
bindable.Event:Connect(function()
func(unpack(args, 1, count))
end)
bindable:Fire()
bindable:Destroy()
end
An event, specifically a BindableEvent, is used to spawn the function in a new thread. Events have no delay (hence “fast”) and emit errors on the first resume of the thread.
Compare this to an almost-matching implementation that uses coroutines:
function fastSpawn(func, ...)
assert(type(func) == "function")
local thread = coroutine.create(func)
local ok, err = coroutine.resume(thread, ...)
if not ok then
-- Insert preferred error-emitting mechanism.
print("ERROR", err)
print(debug.traceback(thread))
end
end
This bit of error handling after the resume is necessary only once. If the thread yields from a Roblox function, then it will be handled by the Roblox scheduler from that point on, and any errors will be emitted as usual. If coroutine.yield is used, then the thread wont be handled any further, which matches the behavior of events.
The only drawback is that an error in the first resume of the thread cannot be emitted to output as an error. We have to make do with just printing it instead. However, let it be known that this information is not lost or inaccessible.