Do ModuleScripts execute without being required?

Hello Everyone!

Quick question. I was reading an article on the Roblox Developer website on DataStores (can be found here) and in the script example, they spawned a local function as well as connected local functions to player events.

This confuses me as I thought that module scripts only execute via the require() function. Do functions that are not included in the module’s table execute without the require() function? Or are my initial thoughts correct?

Sorry, if I make no sense.

Code (for people who don’t want to go to the article):

-- Set up table to return to any script that requires this module script
local PlayerStatManager = {}
 
local DataStoreService = game:GetService("DataStoreService")
local playerData = DataStoreService:GetDataStore("PlayerData")
 
-- Table to hold player information for the current session
local sessionData = {}
 
local AUTOSAVE_INTERVAL = 60
 
-- Function that other scripts can call to change a player's stats
function PlayerStatManager:ChangeStat(player, statName, value)
	assert(typeof(sessionData[playerUserId][statName]) == typeof(value), "ChangeStat error: types do not match")
	local playerUserId = "Player_" .. player.UserId
	if typeof(sessionData[playerUserId][statName]) == "number" then
		sessionData[playerUserId][statName] = sessionData[playerUserId][statName] + value
	else
		sessionData[playerUserId][statName] = value
	end
end
 
-- Function to add player to the 'sessionData' table
local function setupPlayerData(player)
	local playerUserId = "Player_" .. player.UserId
	local success, data = pcall(function()
		return playerData:GetAsync(playerUserId)
	end)
	if success then
		if data then
			-- Data exists for this player
			sessionData[playerUserId] = data
		else
			-- Data store is working, but no current data for this player
			sessionData[playerUserId] = {Money=0, Experience=0}
		end
	else
		warn("Cannot access data store for player!")
	end
end
 
-- Function to save player's data
local function savePlayerData(playerUserId)
	if sessionData[playerUserId] then
		local success, err = pcall(function()
			playerData:SetAsync(playerUserId, sessionData[playerUserId])
		end)
		if not success then
			warn("Cannot save data for player!")
		end
	end
end
 
-- Function to save player data on exit
local function saveOnExit(player)
	local playerUserId = "Player_" .. player.UserId
	savePlayerData(playerUserId)
end
 
-- Function to periodically save player data
local function autoSave()
	while wait(AUTOSAVE_INTERVAL) do
		for playerUserId, data in pairs(sessionData) do
			savePlayerData(playerUserId)
		end
	end
end
 
-- Start running 'autoSave()' function in the background
spawn(autoSave)
 
-- Connect 'setupPlayerData()' function to 'PlayerAdded' event
game.Players.PlayerAdded:Connect(setupPlayerData)
 
-- Connect 'saveOnExit()' function to 'PlayerRemoving' event
game.Players.PlayerRemoving:Connect(saveOnExit)
 
return PlayerStatManager

You’re correct - none of this will run without first requiring the module. I’m assuming this script would run with the assumption that another script requires it first at game startup even though that wasn’t mentioned

2 Likes

Ok, so what about that spawn on the autosave function? Would that function spawn every time I require the module?

When require() is called on a ModuleScript, you are basically running the code inside from beginning to end like any old script. This means you can also spawn functions, create loops, define local functions, and do other things, just like any other script. You don’t only have to return tables of functions/values and things of this style from a ModuleScript.
In fact, you can think of ModuleScripts as a giant function encapsulated in its own Instance, with the only difference being that it has to return one value.

1 Like

And, to answer your question, I believe a function wrapped in a spawn would be created every time you require the module, like you said. It would be like calling the same function over and over.

Correction: I just tested it, and this is not true. If you require a ModuleScript multiple times, it will only run everything in the script once, and never again. Sorry about that…

CorrectionCorrection: I tested further, and if you require a ModuleScript multiple times, it will send its designated return value, but only in its cached form from memory.

e.g., This in a Script:

while wait(1) do
    require(path.to.ModuleScript)
    print("required")
end

and this in a ModuleScript:

return tostring(math.random())

will return in the console for 5 seconds:

0.14421506146025
required
0.14421506146025
required
0.14421506146025
required
0.14421506146025
required
0.14421506146025
required

look down there

3 Likes

To just summarise this:
Code in modules runs only the first time it is required (unless it is cloned, and that is required).
Any further require()s of it will return the current table contents of the module.

4 Likes

Thank you for clarifying and going above and beyond to help me understand. I really appreciate it! :upside_down_face:

1 Like

Thanks for summarizing goldenstein’s post, I appreciate it! :raised_hands:

1 Like

This is actually wrong.

All code inside module scripts run only once.
That means your example would be invalid because it would first calculate the result and then return it any time you require the module script. Actual result:

-- script
local result = require(path.to.ModuleScript)
print(result) --> same result any time you require it again!
-- module
return math.random()

If you wanted it to work like this, you would have to call the function from outside the module:

-- script
local result = require(path.to.ModuleScript)() -- note the extra ()'s
print(result) --> its good now!
-- module
return function()
	return math.random()
end

The way most people use modules is actually to return a table or a function and do stuff in the script which required it. Modules are nothing more than storage which executes all code when required the first time.

Hopefully, this cleared some misunderstandings about modules and didn’t confuse you further.

9 Likes