How to run loop independently?

Is there a way to call in a function inside of a function and not wait until the function is finished to pass on to the next line of code? For example you want to call in two infinite while true loop functions in one function. Since the first while true loop will never end, the second infinite loop function will never be called in. Is there a way to call in a function inside of a function and advance to the next line of code instantly, without having to wait for the completion of the called in function? I tried pcall for this but it doesn’t seem to work, because pcall still waits for the function to error or complete.

2 Likes

You can use coroutines or spawn function (coroutines are recommended over spawn).

spawn(function()
    -- this will run independently
end)

-- or

coroutine.wrap(function()
    -- this will run independently
end)()

Another way to go is to use BindableEvents:

function fastspawn(f)
    local be = Instance.new("BindableEvent")
    be.Event:Connect(f)
    be:Fire()
end

local function test() -- function you want to be invoked independently
    -- something
end

fastspawn(test)

What is more, every function that is connected to an event (BindableEvent, RemoteEvent or other instance events) will run independently when the event is fired.

11 Likes

Thank you. I will try this.
30

1 Like

Which ones allow to pass arguments?

It depends, for example using coroutines you can do it like that

coroutine.wrap(function(a)
    print(a)
end)(5)

and this will print 5

Using the method with bindable events:

function fastspawn(f, ...)
    local be = Instance.new("BindableEvent")
    be.Event:Connect(f)
    be:Fire(...)
end

local function test(a) -- function you want to be invoked independently
    print(a)
end

fastspawn(test, 5)

I have tried this and I doesn’t seem to work (The function is being called, but the arguments are not being passed). Would it be because my function is predefined?

Can you provide the code you are using?

Sure. Basically it is a rainbow overhead GUI that displays a player’s RAP. I want to make it update every x seconds but the function that controls the rainbow must be called in before the update function. The problem is that the rainbow function is infinite, meaning that the update function will never be called in without threading/coroutine.

local HTTP = game:GetService("HttpService")
local BillboardGui = game:GetService("ServerStorage"):WaitForChild("BillboardGui")
local RAPValue = game:GetService("ServerStorage"):WaitForChild("RAPValue")
local t = 1
game:GetService("StarterPlayer").HealthDisplayDistance = 0
game:GetService("StarterPlayer").NameDisplayDistance = 0


function RoundRAP(Value)
	local RAPString = ""
	if Value >= 1000000 then 
		RAPString = ((string.format("%0.1f", tostring(Value/1000000))) .. "M+")
		return RAPString
	elseif Value >= 1000 then
		RAPString = ((string.format("%0.1f", tostring(Value/1000))) .. "K+")
		return RAPString
	elseif Value < 1000 then
		RAPString = (tostring(Value))
		return RAPString
	end
end


function UpdateRAP(player, RAPTable, ClonedGui, ClonedValue, UserNameString)
	local ID = game.Players:GetUserIdFromNameAsync(player.Name)
	local data = HTTP:GetAsync("https://inventory.rprxy.xyz/v1/users/".. ID .."/assets/collectibles", true)
	local DecodedData = HTTP:JSONDecode(data)
	local ItemDataTable = (DecodedData["data"])	
	for i, v in pairs(ItemDataTable) do
		local VTable = (ItemDataTable[i])
		local RAP = (VTable["recentAveragePrice"])
		table.insert(RAPTable, 1, RAP)
	end
	for i, v in pairs(RAPTable) do
		ClonedValue.Value = ClonedValue.Value + v
	end
	ClonedValue.Parent = ClonedGui
	local RAPString = RoundRAP(ClonedValue.Value)
	ClonedGui.TextLabel.Text = (UserNameString .. " | " .. RAPString)
end
		
	
function Rainbow(ClonedGui)	
	warn("Rainbow called in successfully")
	local TweeningInfo1 = TweenInfo.new(t , Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0)
	local TweenObject = ClonedGui.TextLabel
	local Tween1 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(1, 0, 0)})
	local Tween2 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(1, 1, 0)})
	local Tween3 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(0, 1, 0)})
	local Tween4 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(0, 1, 1)})
	local Tween5 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(0, 0, 1)})
	local Tween6 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(1, 0, 1)})
	while true do
		Tween1:Play()
		wait(t)
		Tween2:Play()
		wait(t)
		Tween3:Play()
		wait(t)
		Tween4:Play()
		wait(t)
		Tween5:Play()
		wait(t)
		Tween6:Play()
		wait(t)
	end
end



game.Players.PlayerAdded:Connect(function(player)	
	player.CharacterAdded:Connect(function(character)		
		local RAPTable = {}
		local ClonedGui = BillboardGui:Clone()
		local ClonedValue = RAPValue:Clone()
		local UserNameString = (tostring(game.Workspace:WaitForChild(player.Name)))
		ClonedGui.TextLabel.Text = (UserNameString)
		pcall(UpdateRAP, player, RAPTable, ClonedGui, ClonedValue, UserNameString)
		coroutine.wrap(function(ClonedGui) -- Here is the coroutine
			local TweeningInfo1 = TweenInfo.new(t , Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0)
			local TweenObject = ClonedGui.TextLabel
			local Tween1 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(1, 0, 0)})
			local Tween2 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(1, 1, 0)})
			local Tween3 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(0, 1, 0)})
			local Tween4 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(0, 1, 1)})
			local Tween5 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(0, 0, 1)})
			local Tween6 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(1, 0, 1)})
			while true do
				Tween1:Play()
				wait(t)
				Tween2:Play()
				wait(t)
				Tween3:Play()
				wait(t)
				Tween4:Play()
				wait(t)
				Tween5:Play()
				wait(t)
				Tween6:Play()
				wait(t)
			end
		end)
		ClonedGui.Parent = game.Workspace:WaitForChild(player.Name).Head
	end)
end)

You didn’t pass the arguments to the wrapped function:

coroutine.wrap(function(ClonedGui)
	local TweeningInfo1 = TweenInfo.new(t , Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0)
	local TweenObject = ClonedGui.TextLabel
	local Tween1 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(1, 0, 0)})
	local Tween2 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(1, 1, 0)})
	local Tween3 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(0, 1, 0)})
	local Tween4 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(0, 1, 1)})
	local Tween5 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(0, 0, 1)})
	local Tween6 = game:GetService("TweenService"):Create(TweenObject, TweeningInfo1, {TextColor3 = Color3.new(1, 0, 1)})
	while true do
		Tween1:Play()
		wait(t)
		Tween2:Play()
		wait(t)
		Tween3:Play()
		wait(t)
		Tween4:Play()
		wait(t)
		Tween5:Play()
		wait(t)
		Tween6:Play()
		wait(t)
	end
end)(ClonedGui) -- you forgot to pass the argument here as in my example above where I put 5 in this place

This is happening because the ClonedGui argument in the internal function is a local variable for that function. You have to pass the argument when you are invoking the wrapped function.

1 Like

This works perfectly. Thank you for your help.

1 Like

Also, I have a question. Is it possible to run two coroutines simultaneously in one script?

Yes, just create two coroutines.
Example:

coroutine.wrap(function()
    print("coroutine 1")
end)()
coroutine.wrap(function()
    print("coroutine 2")
end)()

You can learn more about coroutines here:

1 Like

Great. Thank you.
30characters

Does this fastSpawn implementation run the same way as Quenty’s? You could just use his version.

https://github.com/Quenty/NevermoreEngine/blob/version2/Modules/Shared/Utility/fastSpawn.lua

That’s where the idea comes from.

I dont understand what are you trying to say. Yes you can use his version or my version or even implement your own. We aren’t restricted to use the version I provided.

Would it work in the same way though? That’s what my question was asking. Although the idea does come from there, that wasn’t quite what I was asking. Since your implementations are different, wasn’t sure if doing it this way by shorthanding and direct forwarding is also appropriate.

I’m not trying to say anything other than that the option is available. This comment seems somewhat passive aggressive and that’s not really comfortable for me to be receiving. It’s more of a “you could just use his version as well” as in why write a separate one if it’s already available unless there’s a functional difference here?

My last post was not meant to be aggressive, there is no reason I would search the DevForum or the Internet to check if such a simple code has been written before because I can simply write my own implementation in seconds.

1 Like

@TokoNanami
You actually can’t run anything at the same time. Coroutines emulate multi-threadedness, they don’t actually let you have multi-threadedness.

Only one coroutine ever runs at a time, you can’t have two running coroutines. It’s a common misconception that coroutines run things together. You can test this by running this script:

coroutine.wrap(function()
    for x = 1, 5 do
        print(x)
    end

    print(coroutine.running(), 'finished running.')
end)()

coroutine.wrap(function()
    for x = 1, 7 do
        print(x)
    end

    print(coroutine.running(), 'finished running.')
end)()

You’ll notice that it’ll print 1 through 5, then say it’s finished running, before it starts printing the next set. btw, coroutine.running() returns the running thread.

Also, you can run functions like this thing:Thing() using coroutines as well. Since : is just syntax sugar for using a normal . function and passing the thing the function was called on as the first argument, you can take advantage of that and call that type of function using coroutines like so:

-- example function you want to run: "thing:Thing()"
coroutine.resume(coroutine.create(thing.Thing), thing)

Any arguments to resume after create are automatically passed into the create. This is passes self (thing in this case) into thing.Thing, which makes it basically the same as thing:Thing().

I know that coroutines don’t literally run at the same time. I was just trying to answer OP’s question basing on what he was trying to achieve.

1 Like

It’s better to give him the full answer.