As a Roblox developer, it is currently too hard to spawn threads with error handling and spawn
/delay
cannot take additional arguments.
If Roblox is able to address this issue, it would improve my development experience because it would allow me to cleanly spawn a function with arguments but still get proper error handling.
Problem
spawn
and delay
doesn’t accept additional arguments
coroutine.resume
eats errors
xpcall
allows you to get the correct stack trace but can’t be outputted correctly
Ideally I would want to have full control over the arguments given to whatever thread I am spawning without making it impossible to get proper error messages.
Potential solutions
There are a bunch of potential solutions I can come up with. #6 is the most general and most useful one and is the one I would prefer personally.
- Add an option or direct alternative to
coroutine.resume
that doesn’t eat errors (Not a clean solution but decent) - Stop eating errors from
coroutine.resume
(Might be annoying, and could cause lag due to error spam) - Pass extra arguments to
spawn
/resume
(Doesn’t fix most issues, keeps two useless arguments) - Add an option or direct alternative to them that lets you overrride arguments (Not a clean solution)
- Add a new way to spawn threads or functions with the two core problems solved (Fixes most of the problems just fine)
- Add a way to output “fake” errors (e.g. as
debug.error
) or allowerror
to take threads too. (Fixes all the issues, generalized, adds new uses, but most complex)
It could accept a level/thread, just like how error
accepts a level, but wouldn’t kill the caller.
Example:
local success, err = coroutine.resume(thread, ...)
if not success then
debug.error(err, thread) -- Outputs the error like it came from the thread
end
This would also allow you to still return the results assuming the body doesn’t yield which could be very useful.
(P.s. you can prove that threads remember the error location with the new debug.info
function by passing the thread, a 1
for the first level, and then "l"
to get the current line number of the thread)
Uses
Here are some of the examples of why this would be useful.
This eliminates ever having to do error handling and hackily outputting the error with warn
or print
. Could eliminate the need for xpcall
which is bulky.
If you’re utilizing LogService in order to collect errors in your games you don’t need extra code paths for the above
Makes it possible to jump to correct locations by clicking the stack.
Having cleaner errors when spawning threads makes this a lot easier to debug:
-- Assuming #6 this is how you could define betterSpawn (you could assume #5 just replaces this):
local function betterSpawn(func, ...)
local thread = coroutine.create(func)
local success, err = coroutine.resume(thread, ...)
if not success then
debug.error(err, thread) -- Would output the error perfectly (But execution continues below)
end
return success, err
end
local function addChild(child)
-- Stuff
end
-- I use this pattern a lot for things like Players:GetPlayers() and Players.PlayerAdded especially
for _, child in ipairs(something:GetChildren()) do
betterSpawn(addChild, child) -- Spawn it with one of the solutions
end
something.ChildAdded:Connect(addChild)
Assuming you can output custom errors like in #6 you could use this for better debugging in general.
It would also make reverse engineering obfuscated code easier with tools, meaning exploit scripts can be patched faster by developers. This is really useful with loadstring
and/or for spawning “fake” scripts like in my prototype script sandboxing tool (Which is designed to fit a use case like this)