Intoduction
Protected calls, formally known as pcalls, can run functions without erroring and provide if your code was successfully ran but what’s the best way to use them and when should I?
In this tutorial I will be covering the entirety of pcalls and where you’ll need them, so lets begin!
Pcall basics
First and foremost, we should learn what pcall’s syntax really looks like:
pcall(f : function, ...args : any)
Basically, you pass your desired function in as argument 1 and any arguments you want to pass to that function that’s going to be pcall’d as argument 2, 3, 4 etc - Pcall doesn’t mind how many arguments you want to pass to it!
Onto some usecases now.
DataStore errors - Getting/Setting
Roblox DataStoreService, wonderful thing it is saving data between playing sessions.
There are some issues though when trying to Set/Get data which could even lead to data loss - no one wants to join a game and not have their data saved or retrieved.
This is where pcalls come in, you can use them to set/get data and check if it really did.
Commonly people do:
local DataStore = game:GetService("DataStoreService"):GetDataStore("MyData")
local data
local success, response = pcall(function()
data = DataStore:GetAsync("key")
end)
Now this works however you’re creating a variable and a function everytime, when you can reduce that.
Here’s an example of that, I’ll run through it below:
local DataStore = game:GetService("DataStoreService"):GetDataStore("MyData")
local success, response = pcall(
DataStore.GetAsync, --1
DataStore, --2
"key" --3
)
1 - The function for the pcall to run, you can’t call : functions inside a pcall therefore you have to call it with . and now the next arguments are what to pass to the function.
2 - Since you called a : function with . you need to supply what the function belongs to (self) as per OOP rules.
3 - The key of what to Get.
Now the first returned of pcall is a boolean to say if it succeeded, the second however is different;
If it didn’t succeed then it’ll be the error message as a string, you can use that to log why it failed.
If it did succeed then it’ll be what the pcall’d function returned, there can be more than just 1 returned value.
Now setting is very similar except obviously you provide what to set the key to.
Here’s an example of it:
local DataStore = game:GetService("DataStoreService"):GetDataStore("MyData")
local success, response = pcall(DataStore.SetAsync, DataStore, "key", "value")
This also works for UpdateAsync and IncrementAsync.
DataStore errors - retrying
Now since you know how to get/set using pcalls, you’ll need to know how to retry.
This can be achieved using a while or repeat loop, I’ll run through both.
Here’s an example of a while loop, I will talk through it afterwards:
local DataStore = game:GetService("DataStoreService"):GetDataStore("MyData")
local success, response --Don't need to define yet
local count = 0 --1
while not success do --2
if count >= 1 then
warn("Retrying, count:", count, "\nError:", response) --3
wait(7) --4
end
success, response = pcall(DataStore.GetAsync, DataStore, "key") --5
count = count + 1 --6
end
1 - This’ll be our count of how many times we’ve retried.
2 - While success isn’t true then.
3 - This’ll warn us that it’s retrying to get the data.
4 - A cooldown, DataStore limitations.
5 - Set success and data to what the pcall we try returns, do not localise it - the while loop won’t stop trying when it is true since it can’t see the local version of our success.
6 - Increase the count of times we’ve tried
Now a repeat loop will look very similar to this, here’s an example:
local DataStore = game:GetService("DataStoreService"):GetDataStore("MyData")
local success, response
local count = 0
repeat
if count >= 1 then
warn("Retrying, count:", count, "\nError:", response)
wait(7)
end
success, response = pcall(DataStore.GetAsync, DataStore, "key")
count = count + 1
until success
The choice is up to you if either you use repeat loops or while loops, personally I prefer while loops.
Obviously you’ll need to do the same for any other datastore functions.
Roblox web API calls
Other than DataStores, there’s cases in the API were you’ll need to use pcalls, for example GetUserThumbnailAsync has to retrieve the thumbnail off the website - it’s a good idea to pcall if it fails.
StarterGui SetCores
If you’ve used SetCore before you may have seen the ‘<SetCore> has not been registed by the CoreScripts’, this is because the SetCores take time to be initialized therefore you’ll need to pcall it and keep trying.
Here’s a small example:
local sg = game:GetService("StarterGui")
local success
while not success do
success = pcall(sg.SetCore, sg, "TopbarEnabled", false)
wait(1) --add a wait to ensure no Game script timeouts :P
end
xpcall
The Lua global xpcall allows for you to provide a error handler as the second argument.
The parameter to the error handler will be the error message too!
Keep in mind xpcall cannot yield, do not call any wait()s etc inside one.
Here’s an example using SetCore:
local sg = game:GetService("StarterGui")
local success
function displayError(err)
warn("An error occurred:", err)
end
while not success do
success = xpcall(sg.SetCore, displayError, sg, "TopbarEnabled", false)
wait(1)
end
ypcall
Back in the day, pcalls couldn’t yield so Roblox created the yielding protected call.
This is now useless as pcalls can yield nowadays, you can use it if you’d like to however it makes no improvement.
debug.traceback
When an error happens, if you’d like to know where exactly it happened then use debug.traceback()
and it’ll return the current stack of where you called it as a string.
Here’s a small example:
local sg = game:GetService("StarterGui")
local success
function displayError(err)
warn("An error occurred!\nError:", err, "\nTraceback:", debug.traceback())
end
while not success do
success = xpcall(sg.SetCore, displayError, sg, "ResetButtonCallback", false)
wait(1)
end
Moving on from here
Now that you’ve learnt and hopefully understood the process of pcalls and retrying you can ensure no unexpected API errors which could’ve been stopped!
Helpful links
These links can help you if you still need to learn more or if you’d just like to read more about pcalls:
debug.traceback:
Every function you’ll need to pcall
There may be more than this, but here’s a list of functions that will need to be pcall’d:
List
Thanks for reading!
This was my second community tutorial, hope you did enjoy.
Please make sure to reply if you have anything to add or even correct me!