Data store fails to save when player leaves within 3 seconds of joining

local DataStoreService = game:GetService("DataStoreService")
local PlayerStore = DataStoreService:GetDataStore("PlayerStore")

local Tries = 3

local PlayerValues = {
	Money = 0,
	Level = 1,
	EXP = 0
}

function generateData(Player)
	for i, v in pairs(SessionData) do
		if i == 1 then
			Player.Data.Money.Value = v
		elseif i == 2 then
			Player.Data.Level.Value = v
		else
			Player.Data.EXP.Value = v
		end
	end
end

function loadData(Player)
	local Count = 0
	local Folder = Instance.new("Folder")
	Folder.Parent = Player
	Folder.Name = "Data"
	for i, v in pairs(PlayerValues) do
		local Value = Instance.new("IntValue")
		Value.Parent = Folder
		Value.Name = i
		Value.Value = v
	end
	repeat
	wait(0.2)
	local Success, Err = pcall(function()
		SessionData = PlayerStore:GetAsync(Player.UserId)
	end)
	if Success then
		for i, v in pairs(SessionData) do
			print(i, v)
		end
		print("Success")
		generateData(Player)
		return
	else
		print("Error")
		Count = Count + 1
	end
	until Count >= Tries
end

function saveData(Player)
	local Storage = {}
	local Count = 0
	for i, v in pairs(Player.Data:GetChildren()) do
		Storage[i] = v.Value
	end
	repeat
		wait(0.2)
		local Success, Err = pcall(function()
			PlayerStore:SetAsync(Player.UserId, Storage)
		end)
		if Success then
			print("Success")
			return
		else
			print("Error")
			Count = Count + 1
		end
	until Count >= Tries
end

game.Players.PlayerAdded:Connect(function(Player)
	loadData(Player)
	while true do
		wait(1)
		Player.Data.Money.Value = Player.Data.Money.Value + 1
	end
end)

game.Players.PlayerRemoving:Connect(function(Player)
	saveData(Player)
end)

The loadData() works perfectly fine . But the saveData() seems to only work when the player has joined the game and stayed for like at least 6 seconds. Is this common with data stores?

Well one thing I wanted to point out is that you’re waiting .2 seconds ( or even more since it’s not accurate ) before actually saving the data and also I noticed some stuff in your scripts so I decided to go ahead and write it up again :

local DataStoreService = game:GetService("DataStoreService")
local PlayerStore = DataStoreService:GetDataStore("PlayerStore")
local Players = game:GetService("Players")
local Tries = 3

local PlayerValues = {
	Money = 0,
	Level = 1,
	EXP = 0
}

function generateData(Player,Data)
    Data = Data or PlayerValues -- Basically if Data doesn't exist meaning player first time joined then use the default values.
    for i,v in pairs(Data) do -- using pairs because Data is supposed to be a dictionary.
        local CurrentStat = Player.Data:FindFirstChild(i) -- since this is a dictionary i would be the index which is the key ( Money, Level or EXP ). 
        print(i) -- just so you can see lol

        if not CurrentStat then warn(string.format("couldn't find stat inside player data : %s",i)) continue end
        CurrentStat.Value = v -- v is the value of the stat 
    end
end

function loadData(Player)
    local Count = 0
    local Folder = Instance.new("Folder")
    Folder.Parent = Player
    Folder.Name = "Data"

    for i, v in pairs(PlayerValues) do
		local Value = Instance.new("IntValue")
		Value.Parent = Folder
		Value.Name = i
		Value.Value = v
	end

    -- Now here you were doing repeat, there is actually no need to do that unless the data didn't load successfully so : 
    local success,data = pcall(PlayerStore.GetAsync,PlayerStore,Player.UserId)
    if not success then -- datastore failed or whatever
        -- If the data didn't load successfully THEN do a repeat and do the 3 attempts at getting data... 
        repeat wait(.2) 
            success,data = pcall(PlayerStore.GetAsync,PlayerStore,Player.UserId) 
            Count = Count + 1 
        until success or Count >= Tries
        -- if it still didn't get after 3 tries then kick the player to prevent any data loss ( which shouldn't occur in the first place )
        if not success then return (function() Player:Kick("To prevent data loss you've been kicked from the game.") end)() end
    end
    generateData(Player,data) -- Generate the data like in your function
end

function saveData(Player)
    local Storage = {}
    local Count = -2 -- I made the count -2 because It's VERY IMPORTANT to save the data... ( I wouldn't even have a count if I were you and just repeat until success )
    for _,v in pairs(Player.Data:GetChildren()) do
        Storage[v.Name] = v.Value -- yes
    end
    -- Now here you were also waiting .2 seconds or even more before actually saving data which I think was your main issue :
    local success = pcall(PlayerStore.SetAsync,PlayerStore,Player.UserId,Storage)
    if not success then
        repeat wait(.2) 
            success = pcall(PlayerStore.GetAsync,PlayerStore,Player.UserId) 
            Count = Count + 1 
        until success or Count >= Tries
        -- can't really kick a player for "data loss prevention" if his data didn't save successfully and they already closed their client (kinda)
    end
end

Players.PlayerAdded:Connect(loadData)
Players.PlayerRemoving:Connect(saveData)

game:BindToClose(function() -- incase the game shutdown or something happened
    for _,v in ipairs(Players:GetPlayers()) do
        coroutine.wrap(saveData)(v)
    end
    wait(10)
end)

You have to do more fixing the to the script I’m providing, I suggest looking at articles made by other devforum members about datastores such as :

and others by researching.

I believe this may be caused by the wait()s you have