Hello. I would like to know if there’s a way to never return a function so that the code that called it doesn’t continue. Traditionally, you would return from the place that handles the returned value but I would like to do this from the function instead so that I don’t have to repeat the same return line…
if value == nil then return end
…every time the function is used. Complete annotated sample:
local function someFunction()
local valueToReturn = {}
if false == false then
valueToReturn = nil
end
return valueToReturn -- nil will be returned, but...
-- nil will cause an error in the code below if not stopped. therefore...
-- I want this function to stop and never return
end
local value = someFunction()
if value == nil then return end -- traditionally, this is how a bad value is stopped. but it is verbose to write this every time we use the function. is there a way to do this in the function itself?
value.property = true -- if not stopped, an error will occur because the value is nil
local function someFunction()
return nil
-- want this to never return
end
local value = someFunction()
if value then
value.property = true -- just do the opposite
end
That is what I am doing at the moment.
But if I do this several times…
local value = someFunction()
if value then
value.property = true
end
local value = someFunction()
if value then
value.property = true
end
local value = someFunction()
if value then
value.property = true
end
local value = someFunction()
if value then
value.property = true
end
local value = someFunction()
if value then
value.property = true
end
See how I have to state
if value then
...
end
every time? I want to do this from the function instead.
What’s your use case for this? Perhaps there’s some better way to do this.
However, for your example you could do something like instead of returning the value then setting it, you just set it in the function. This would save you from having to type it out every time after the function.
For example:
local function setValue()
if someValue then
value.Property = true
end
end
Don’t mind the changing of the property, that is irrelevant and just there to show that the code must be stopped otherwise it will error.
It’s for any use case where a function that can potentially return a nil value is being called several times in a place that will error if not stopped. The way I’m doing it at the moment works but I’m wondering if there’s a more efficient way of doing it so that I don’t have to state
if value == nil then return end
every time in the origin code, and rather only do this check in the function itself and never return if it is nil:
local function someFunction()
local valueToReturn = 5
if false == false then
valueToReturn = nil
end
-- the value is now nil
if valueToReturn == nil then
-- never return!!
else
return valueToReturn
end
end
Functions can’t be cancelled when called unless you write the boilerplate to simulate a cancellation by preventing subsequent code from running. Same goes with preventing functions from returning (which, if a function finishes, it will always return something or void).
There’s no easy-way-out like what you’re looking for in the thread without writing the verbosity or framework to handle that. Depending on your circumstances though, you could get close enough to this by incorporating Promises into your code.
Promises are cancellable, though in the context of Promises cancellations are applicable to Promise chains and not the functions themselves, that’d still be on you to write code for. You’d mainly be looking to create chains to handle your processes then. For example, your existing code could be translated as:
Promise.new(function (resolve, reject, onCancel)
-- Do something
if onCancel(function ()
-- Handle cancellation
end) then return end -- Subsequent code doesn't execute
resolve(true)
end):andThen(function (resolutionValue)
value.property = resolutionValue
end)
Disclaiming that I don’t cancel my Promises right now so I’m not well-versed in deploying cancellation handlers. Best read the documentation and do some experimenting if you’re interested.
you could error, that will cancel up until you have a pcall or thread, I think more context is needed though as this sounds very silly to not handle potential nil values
Thank you, this is exactly the confirmation I was looking for. I didn’t believe it was possible but I thought I’d ask in case there was a better way I could be doing this. Really sucks that this can’t be done in a convenient way, it’s really annoying to have to state the same thing over and over when it could obviously be simplified.
Yeah, I guess erroring is another way but that’s a bit messy. Like colbert said, I don’t think there’s any real nice way of doing it.
Nil values would be handled in the function.
Call coroutine.yield() where you want the code to end. It does exactly what you asked for. The function will never return, it will never finish.
It’s exactly like wait() except instead of continuing after some time, it just stops and never starts again.
It’s literally “stop here and wait to be resumed” except you haven’t told anything to resume the thread so it never gets resumed.
I really recommend that you throw an error and handle it at a favorable point, however.
Wrap the entire thing you’re doing in a pcall and simply let the error happen.
Either do something like assert(valueToReturn) or if not valueToReturn then error() end in the function, or just calmly return nil and cause an error elsewhere.
local function doTheThing()
someFunction().property = true
someFunction().property = true
someFunction().property = true
end
pcall(doTheThing)
-- or simply
pcall(function()
someFunction().property = true
end) -- etc.
It will not make an error message because it has been caught and ignored.
The downside is an extra level of indentation and a wrapping, as it seems you’re doing this on the left edge of a script and not in a function of some sort.
Ahh yes, I never thought about using coroutine. Thanks for recommending that. Do you know if there are any performance implications to using coroutine.yield(); does it act as an infinite wait or does it actually stop?
If I were to take the coroutine option (which I wouldn’t and I’ll just handle the error like @Eestlane771 said), I wouldn’t just coroutine.yield() and leave there, while that thread won’t be using any processing power, it will still occupy space in the memory, so what I would instead do is call task.cancel(thread) to kill the thread and have no fear for memory leaks.
And just for the sake of clarity, it’s not exactly like wait(), the only thing they share in common is yielding and that’s about it.
Yes, memory was something I was concerned about. I’d rather not use the pcall method because it’s messier and occupies more lines than necessary.
I’m actually not really sure how the coroutine method is even working. I didn’t even have to wrap the function in coroutine.wrap and it’s just… working (no errors, either). So I’m not really sure what I would pass for the thread argument in task.cancel.
Edit: Never mind, I did task.cancel(coroutine.yield()) and that seems to have worked.
That should not work, coroutine.yield() does not return you the thread, so I really don’t know what you are canceling with that, you need to do something like this:
local thread = coroutine.create(yourFunction())
coroutine.resume(thread) -- start the thread because coroutine.create doesn't run it for you
-- Whenever you want to cancel it, do this
task.cancel(thread)
I’m confused as to why it works too, but it does, and it’s not producing any errors… here are the functions I’m applying this to:
GetPlayerInventory module
local PlayerInventories = require(game.ServerScriptService.Shop.SessionData.PlayerInventories)
return function(player)
local inventory = PlayerInventories[player.UserId]
inventory = nil -- simulate inventory not loading (for testing purposes)
if not inventory then
player:Kick(
'\n\n'..
[[Your inventory failed to load. To prevent data loss, you were kicked.
Please try joining again. If the issue persists, contact support.]]
)
task.cancel(coroutine.yield()) -- dont return
end
return inventory
end
GetPlayerInventory called by NewCharacter module:
return function(character)
local player = game.Players:GetPlayerFromCharacter(character)
if not player then return end
-- give player inventory items
for sku, playerItemInfo in pairs(GetPlayerInventory(player)) do -- function called here
...
end
end
I think I know why that works, the argument itself is doing the yield:
local function test(arg)
print("Hello") -- This never prints
return true
end
print(test(coroutine.yield())) -- So naturally this won't print either
I don’t know if the yield happens at test(coroutine.yield()) or when arg is being retrieved, I think it’s the former. So with this you are still causing memory leaks, which is why you need to use coroutine.create().