FindFirstChild() is it necessary?

I have the habit of using FindFirstChild whenever I’m trying to get the child of something, is it necessary? I see a-lot of people who don’t do it but when I was learning to script every tutorial I watched they would use it too.

3 Likes

I think it’s useful for avoiding errors. I also use WaitForChild.

2 Likes

FindFirstChild is sometimes necessary but you should not use it if you don’t have to since it has big performance issues. WaitForChild is a good replacement for times when you are sure that it is going to be a child.

3 Likes

It is really necessary when trying to verify if an instance exists to execute the following actions, otherwise it will not. It will return the instance if FindFirstChild finds the child, otherwise it will return nil.

3 Likes

This is somewhat misleading, it’d be more accurate to say that indexing a child is faster than using :FindFirstChild()
For example:

local part = workspace.Part;

Is slightly faster than

local part = workspace:FindFirstChild("Part");

You’ll only see this difference if you’re calling :FindFirstChild() many times in short intervals, like a loop.

2 Likes

yes bec when u use FindFirstChild it gets the object first without having to wait

FindFirstChild is only needed for an if statement. Otherwise you should only directly index children. That is of course with the exception of waiting for a child to exist via WaitForChild, which is necessary if dealing with instances at the start of the game.

Actually FindFirstChild does have a use with its second argument, if set to true. This will perform a recursive search that will check all descendants instead of just the immediate children. This is the only other use-case I see.

2 Likes

FindFirstChild being slower is a performance issue, even Roblox says so:
image

Here’s my attempt at summarizing it all.

FindFirstChild is best for checking whether or not the child actually exists, when whether it does exist varies, so the script can handle both cases. This mainly applies to objects that can be destroyed and replaced. I also used to rely on it for getting children with names containing spaces before Parent[“Child Name”] became a thing, or at least, before I learned of it, but that no longer is necessary.

If you expect the child to exist at all times, FindFirstChild isn’t necessary at all. If the child is in a client-sided location, you likely should use WaitForChild in case it hasn’t loaded in yet, because the client isn’t guaranteed to have everything ready before scripts begin to run. This also applies in any other case where you expect a child to exist, but not immediately.

1 Like

performance dependent code.

Roblox agrees with me here. Once again, saying it takes 20% longer can paint the picture that it can take a noticeable amount of time to compute when in reality, it’s a hundredth of a second difference in time.

WaitForChild is not limited to the client. It should be used any time a script is expected to run on start. It should also be used for PlayerAdded and CharacterAdded, or any other event in which a more complex model is added. The only time this wouldn’t be needed in the case of a complex model being added is if it’s a clone on the same thread.

1 Like

Interesting. So far I have rarely if ever needed to use it elsewhere to avoid errors, but I will keep that in mind. Thanks!

I probably should add that FindFirstChildOfClass is another option, when and where one expects the object in question to be the only of its class within its parent. I use this for Humanoids nowadays. Granted, I no longer rely on differently-named humanoids to differentiate player characters’ humanoids from enemies’ humanoids in combat games, but it can help. It’s definitely useful for running commands in Studio.

In an ideal world you would never be allowed to index children without FindFirstChild. Since that ship has sailed, it’s up to you. It’s not particularly necessary but it could help you dodge some trouble that you might get when using dot syntax as the first post mentioned.

The thing about Roblox is that dot syntax can be used to access both properties and children so these often find themselves with namespace conflicts. You can take a look at a few controversial property updates and code support questions – since instances may share the same name as a property and properties have precedence when indexing, you might unexpectedly get the wrong thing.

Despite this, don’t let the “20% slower” comment scare you. Even if it’s slower by a significant margin, you should consider other factors like how many children are in the instance you’re using FindFirstChild on (or descendants in the case of setting the second argument to true). The engine can still execute FindFirstChild fast.

So is it necessary? We’re not in that ideal world, so no. It may also be unreadable to you or your collaborators (daisy chaining still has the same problem as dot syntax if a step in the chain is nil). Your use case for FindFirstChild will boil down to indexing dynamically created instances you can’t guarantee will exist when the code runs (i.e. character’s Humanoid).

5 Likes

Find first child shouldn’t be used in all situations due to the fact it takes 8 times longer.

Here’s a summary of what it does in case you’re wondering though.

Find First Child will return a value of nil if the object doesn’t exist, which is quite useful at avoiding errors but isn’t necessary to be used constantly as a habit. If the object does exist, it will return the value of the object.

For example:

local hi = workspace:FindFirstChild("bye")

if hi then
print("bye!")
end

Basically, in a short sentence, it just checks if the object exists.

WaitForChild() just waits for the object to load before continuing the script, when exceeded 5 seconds, will print a warn message.

The wiki says it’s worse than FindFirstChild

Use FindFirstChild for something like if he has keys, or touched to see if it has humanoid, etc.

It really depends, I only use FindFirstChild() if it is a temporary Child, else if it is a permanent Child I don’t use it.

@yname I figure you would also like to know about these results.
So I’ve done some testing and have come across some interesting results.
At 50 million calls for a part asdfghjklkjhgfdsa, WaitForChild is consistently faster than FindFirstChild @geometricalC2123

  19:19:10.069  Testing Benchmark Results:  -  Client - Timer:59
  19:19:10.069  Timer WaitFor: 9.072540699969977 secs  -  Client - Timer:22
  19:19:10.070  Timer FindFirst: 9.315383000299335 secs  -  Client - Timer:22

Over 120 million calls for that same name, dot tends to be faster than bracket

  19:26:01.843  Testing Benchmark Results:  -  Client - Timer:59
  19:26:01.843  Timer Bracket: 8.576460700016469 secs  -  Client - Timer:22
  19:26:01.843  Timer Dot: 8.532256300095469 secs  -  Client - Timer:22

sometimes bracket wins, other times dot wins.
If you are wondering how the dot/brack and the finder functions compare over the same 50mil calls

  19:37:56.623  Testing Benchmark Results:  -  Client - Timer:59
  19:37:56.624  Timer Bracket: 7.963528700172901 secs  -  Client - Timer:22
  19:37:56.624  Timer Dot: 7.975315900053829 secs  -  Client - Timer:22
  19:37:56.624  Timer WaitFor: 9.235567100346088 secs  -  Client - Timer:22
  19:37:56.624  Timer FindFirst: 9.557014799676836 secs  -  Client - Timer:22

Now I’ve got something really interesting for you. What if we just look for a part a? Same # of trials, just a shorter name:

  19:41:57.714  Testing Benchmark Results:  -  Client - Timer:59
  19:41:57.714  Timer Bracket: 3.4464027998037636 secs  -  Client - Timer:22
  19:41:57.714  Timer Dot: 3.417652999982238 secs  -  Client - Timer:22
  19:41:57.714  Timer WaitFor: 4.808735299855471 secs  -  Client - Timer:22
  19:41:57.714  Timer FindFirst: 5.248661700170487 secs  -  Client - Timer:22

A consistent ~50% performance increase over asdfghjklkjhgfdsa

My test code:

local Bench = require(game.ReplicatedStorage.Timer).NewBench("Testing")
wait(5)
local trials = 5e7
--asdfghjklkjhgfdsa
Bench:NewTimer("Bracket"):Start()
for i=1,trials do
	local thing = workspace["a"]
end
Bench:StopCurrentTimer()
wait()

Bench:NewTimer("Dot"):Start()
for i=1,trials do
	local thing = workspace.a
end
Bench:StopCurrentTimer()
wait()

Bench:NewTimer("WaitFor"):Start()
for i=1,trials do
	local thing = workspace:WaitForChild("a")
end
Bench:StopCurrentTimer()
wait()

Bench:NewTimer("FindFirst"):Start()
for i=1,trials do
	local thing = workspace:FindFirstChild("a")
end
Bench:StopCurrentTimer()
wait()

Bench:Results()
6 Likes

I’m pretty sure you will never call WaitForChild 50 million times ever in game development right?

1 Like