So I created an automatic gun that fires. It works fine and all, but the thing is, when I rapidly click the mouse, the while canFire == true do loop stacks on each other, thus creating a super-fast, insta-kill weapon. Yikes.

Here's my LocalScript inside of one such example. (yes, it's long.)

script.Parent.AncestryChanged:Connect(function()
	local tool = script.Parent
	local player = game:GetService("Players").LocalPlayer
	
	local canFire = false
	
	local maxAmmo = 30
	local ammo = maxAmmo
	local Reloading = false
	
	local uis = game:GetService("UserInputService")
	
	local player = game.Players.LocalPlayer or game.Players.PlayerAdded:Wait()
	local character = player.Character or player.CharacterAdded:Wait()
	local humanoid = character:WaitForChild('Humanoid')
	
	local reloadAnim = humanoid:LoadAnimation(script.Parent.Animation)
	local fireAnim = humanoid:LoadAnimation(script.Parent.fire)
	
	tool.Equipped:Connect(function(mouse)
		canFire = true
		uis.MouseIconEnabled = false
		player:WaitForChild('PlayerGui'):WaitForChild('GunMouse').Enabled = true
		
		script.Parent.Unequipped:Connect(function()
			uis.MouseIconEnabled = false
			player:WaitForChild('PlayerGui'):WaitForChild('GunMouse').Enabled = false
		end)
		
		mouse.Button1Down:Connect(function()
			canFire = true
			while canFire == true and ammo > 0 and Reloading == false do
				--<< COSMETICS >>--
				script.Parent.Handle.fire:Play()
				fireAnim:Play()
				ammo = ammo - 1
				script.Parent.Handle.Part.MuzzleEffect.Enabled = true
				wait()
				script.Parent.Handle.Part.MuzzleEffect.Enabled = false
				
				script.Parent.Handle.BillboardGui.Frame.TextLabel.Text = ammo.." / "..maxAmmo
				--<< RAYCAST >>--
				local ray = Ray.new(tool.Handle.Part.CFrame.p, (mouse.Hit.p - tool.Handle.Part.CFrame.p).unit * 300)
				local part, position = workspace:FindPartOnRay(ray, player.Character, false, true)
				
				local beam = Instance.new("Part", workspace)
				beam.BrickColor = BrickColor.new("White")
				beam.FormFactor = "Custom"
				beam.Material = "Neon"
				beam.Transparency = 0
				beam.Anchored = true
				beam.Locked = true
				beam.CanCollide = false
				
				local distance = (tool.Handle.Part.CFrame.p - position).magnitude
				beam.Size = Vector3.new(0.1, 0.1, distance)
				beam.CFrame = CFrame.new(tool.Handle.Part.CFrame.p, position) * CFrame.new(0, 0, -distance / 2)
				
				
				if part then
					local humanoid = part.Parent:FindFirstChild("EnemyHum")
					if humanoid then
						game.StarterPlayer.Hit:Play()
						player.PlayerGui:WaitForChild('Hitmarker').Enabled = true
						humanoid:TakeDamage(30)
						wait()
						player.PlayerGui:WaitForChild('Hitmarker').Enabled = false
					end
				end
				
				for i = 0,1,.25 do
					beam.Transparency = i
					wait()
				end
				
				game:GetService("Debris"):AddItem(beam, 0.1)
			end
		end)
		mouse.Button1Up:Connect(function()
			canFire = false
		end)
		uis.InputBegan:Connect(function(keyCode)
			if keyCode.keyCode == Enum.KeyCode.R then
				if ammo <= maxAmmo then
					if Reloading == false then
						reloadAnim:Play()
						Reloading = true
						script.Parent.Handle.magOut:Play()
						wait(script.Parent.Handle.magOut.TimeLength)
						script.Parent.Handle.magIn:Play()
						wait(script.Parent.Handle.magIn.TimeLength)
						script.Parent.Handle.reload:Play()
						wait(script.Parent.Handle.reload.TimeLength)
						script.Parent.Handle.BillboardGui.Frame.TextLabel.Text = "30 / 30"
						ammo = maxAmmo
						Reloading = false
					end
				end
			end
		end)
	end)
end)

I've tried using other methods like RunService.RenderStepped, but to no avail.

Please help me!