Use task.spawn() for fetching humanoids of players?

I’m looking at two different excerpts of code from the Roblox ‘Core’ and ‘Gameplay Scripting’ tutorials, in which the two use different ways of fetching the Humanoid object of Players for their respective reasons.

One uses task.spawn() while the other doesn’t. But why?

The following is from the Create Player Hazards section of Core tutorial:

local function onPlayerAdded(player)
  -- Reset player coins to 0
  updatePlayerCoins(player, function(_)
    return 0
  end)

  player.CharacterAdded:Connect(function(character)
    -- WaitForChild would stop the player loop, so below should be done in a separate thread
    task.spawn(function()
      -- When a player dies
      character:WaitForChild("Humanoid").Died:Connect(function()
        -- Reset player coins to 0
        updatePlayerCoins(player, function(_)
          return 0
        end)
      end)
    end)
  end)
end

-- Initialize any players added before connecting to PlayerAdded event
for _, player in Players:GetPlayers() do
  onPlayerAdded(player)
end

And the following is from a script of the Gameplay Scripting tutorial:

local Players = game:GetService("Players")

local setupHumanoidAsync = require(script.setupHumanoidAsync)

local function onCharacterAdded(player: Player, character: Model)
	local humanoid = character:WaitForChild("Humanoid")
	setupHumanoidAsync(player, humanoid)
end

local function onPlayerAdded(player: Player)
	-- Call onCharacterAdded if the player already has a character
	if player.Character then
		onCharacterAdded(player, player.Character)
	end
	-- Call onCharacterAdded for all future character spawns for this player
	player.CharacterAdded:Connect(function(character: Model)
		onCharacterAdded(player, character)
	end)
end

-- Call onPlayerAdded for any players already in the game
for _, player in ipairs(Players:GetPlayers()) do
	onPlayerAdded(player)
end

As far I can tell, the only difference between the two is an extra layer of the onCharacterAdded function in the second case, but I don’t see how it correlates to not needing task.spawn() since the original function in the for loop needs the function to return before going to the next iteration, and a bad case of WaitForChild will cause a loop delay without a task.spawn().
This raised some questions about how luau executes code and why task.spawn() is necessary in the first case and not the second.

The first section doesn’t actually need task.spawn since .PlayerAdded would run on a new “thread”. Not sure why they put it in there, but keep in mind the developer hub has some errors.

2 Likes

That’s it! I trust you’re okay with me adding something to this.

These threads Katrist is talking about are created by Connect and Once, so they are reasonably similar to coroutines and task methods. Green threads, distinct from execution in parallel, are carefully considered and managed by the task scheduler, which resumes one thread at a time.

The dev hub generally has some pretty fine tutorials. These two could use some slight changes though.

I guess in both examples, if it’s clear that the function is going to yield, it would make much more sense to simply wrap each call in the loop in its own thread.

for _, player in Players:GetPlayers() do
	task.spawn(onPlayerAdded, player)
end
Players.PlayerAdded:Connect(onPlayerAdded)

You are absolutely right that unexpected yielding could cause race conditions and break something.

Other than that...

…from the organisational point of view, a finer approach to what the first example does might be to split code into delegated modules responsible for various tasks. Perhaps a module handling the live data would be more suitable for production code, breaking the process into multiple steps, such as initialising the player, reading the fetched data, creating leaderstats and values, making sure they are ready for the first updates.

Merry christmas!

2 Likes

Great explanation! I understand a bit more about the thread implementation/usage now.
Happy holidays!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.