So I read a post on scripting helpers a few months ago (can’t remember which one) where it was mentioned that :GetAsync returns nothing, not even nil, when there is no data. Which surprised me since it is convention that nil is returned if nothing useful is able to be returned.
Functions like Instance::FindFirstChild(string name[, bool recursive]), Instance::FindFirstAncestor(string name), Instance::WaitForChild(string name[, double timeout]) are a few examples that return nil if no instance called name is found. So why does GlobalDataStore::GetAsync return nothing?
If you execute this in command bar, assuming you don’t have a data store named "test" and therefore no data on the key "hi" you will get 0 in the output which means :GetAsync returned nothing. Not even nil.
This could be intentional. The post I read was made 6 years ago. It isn’t necessarily an issue but I was wondering why, since getter functions always return something. That is their nature. Btw that would be in engine bugs
No, you’ve misinterpreted the docs. It is meant to say if there is no value in data stores for that key then it would return nil. But this is in fact not the case. Just because what the docs says it returns does not match what it actually does return (nothing) I might as well make a bug report for it.
It appears this is a bug, albeit not one of huge concern.
For all intents and purposes, GetAsync returns nil when there is no value set. Like you mentioned, you can think of the function as just returning nothing (different from returning nil) when no value is set.
This behavior is very prevalent in common code. Consider a function like this:
local function test()
-- return a value if everything goes as planned, if not, just let the function reach the end (return nothing)
print(select('#', test())) --> 0
print(type(test())) --> error
When you compare the value to nil, it returns true. If you run an if statement to check if the value exists (positive outcome), it will return false. Thus, it behaves, for the most part, like nil. The only caveat is, like you observed, there is literally nothing returned. Thus, calling select will result in 0 arguments, and calling a function like type() will error because it expects a parameter but it gets nothing.
It should also be noted that the moment you assign the result from a function that doesn’t return anything (like GetAsync when no value is set or the function shown above) to a variable, that variable is not ‘nothing’ but nil, so it will result in what you’re looking for with select.
local a = game:GetService("DataStoreService"):GetDataStore("test"):GetAsync("hi")
print(select('#', a)) ---> 1
print(type(a)) ---> nil
For the last part that is what I tend to do. Store the result of function calls in variables. I do believe this is just API inconsistency so I’m gonna make a small bug report about it. Of course it is nothing big but who doesn’t want consistency
Speaking of which, the good thing is that voided values just turn into nil when assigning to something and when they are behind a comma. Voided values in lua are particularly funny because they only exist temporarily as a result of not actually having anything in place of the return value, meaning that the only problems that would really be caused arise from things like counting/iterating through return values and printing directly to the output, which is usually something you never do with GetAsync anyways.