How can I simplify "exiting out of a function call"

I have this setup here

local cancel_function = false

local function hi()
	print("Hello")
	
	for i = 1, 3 do
		if cancel_function then
			return
		end
		
		task.wait(1)
	end
	
	print("World")
	
	for i = 1, 3 do
		if cancel_function then
			return
		end

		task.wait(1)
	end
	
	print("!")
	
	for i = 1, 3 do
		if cancel_function then
			return
		end

		task.wait(1)
	end
end

You’re able to see that I’m trying to make it possible cancel the function midway in the script because the function yields. Is there any easier way that I can simplify this?

1 Like

I am understanding what your doing but what I’m failing to understand is why. what use case would this be needed.If your having a problem with yielding I guess you find out whats causing it to yield could be waits. try to dig into the problem right now your over complicating a simple problem

1 Like

do courintine.yeild() that’ll stop the function

1 Like

You can drag the for loop inside of a while true do loop, i think

spawn(function() --Please use a coroutine though
    while true do
        for i = 1,3 do
            if cancel_function then return end --i put it all on a single line :)
            wait(1)
        end
        print("boo")
    end
end)
1 Like

I have a minigame with a crap ton of waits in it

-- this function is called an infinite number of times

function Round:Init()
    local GameService = Knit.GetService("GameService")
    
    local function enough_players()
        return #Players:GetPlayers() >= 2
    end

    local function show_not_enough_players(plr)
        if not enough_players() then
            GameService.Client.NotEnoughPlayersSignal:FireAll()
        end
    end

    local connection = Players.PlayerAdded:Connect(show_not_enough_players)

    show_not_enough_players()
    self._janitor:Add(self.Instance)

    repeat task.wait() until enough_players()

    connection:Disconnect()
    for _, plr in ipairs(Players:GetPlayers()) do
        GameService.Client.CleanUpNotEnoughPlayersSignal:Fire(plr)
    end

    -- waiting for players
    for i = 10, 1, -1 do
        GameService.Client.ClientPrint:FireAll("Waiting for players ", i)
        task.wait(1)
    end

    self:AddPlayersToRound()
    self:ObservePlayersInRound()

    GameService.Client.ClientPrint:FireAll(self.Warden.Name, " has been selected as the warden for this round!")

    -- give warden options for the first activity
    GameService.Client.GiveWardenOptions:Fire(self.Warden, Round.Activities)

    -- 20 second timeout until random activity chosen
    local activity_chosen
    local choose_activity_connection

    choose_activity_connection = GameService.Client.SetFirstActivity:Connect(function(player, activity_index)
        if (not Round.Activities[activity_index]) or (not Round.Activities[activity_index]._activity_type == "Initial") then return warn("Not a valid activity from, ", player.UserId) end
        
        activity_chosen = Round.Activities[activity_index]
    end)

    for i = 20, 1, -1 do
        GameService.Client.ClientPrint:FireAll("Warden choosing the first activity... ", i)

        if activity_chosen then
            GameService.Client.ClientPrint:FireAll("Warden selected activity '", activity_chosen._name,"'!")
            task.wait(2)
            break
        end

        task.wait(1)
    end

    choose_activity_connection:Disconnect()
    Knit.GetService("GameService").Client.CleanActivityGui:Fire(self.Warden)

    if not activity_chosen then
        local initial_activities = {}

        for _, activity in ipairs(Round.Activities) do
            if activity._activity_type == "Initial" then
                table.insert(initial_activities, activity)
            end
        end

        activity_chosen = initial_activities[math.random(1, #initial_activities)]
        GameService.Client.ClientPrint:FireAll("Random activity selected, '", activity_chosen._name,"'!")
    end

    self._first_activity = activity_chosen
    self._current_activity = activity_chosen

    self:RespawnPlayersInRound()

    -- intermission
    for i = 10, 1, -1 do
        GameService.Client.ClientPrint:FireAll("Intermission ", i)
        task.wait(1)
    end

    every_player(function(plr)
        GameService.Client.ClientPrint:FireAll("Round begins now!")
    end)

    for _, line in pairs(self._first_activity._instructions) do
        GameService.Client.ClientPrint:FireAll(line)
        task.wait(5)
    end

    for i = 3, 1, -1 do
        GameService.Client.ClientPrint:FireAll("Cell doors open in... ", i)
        task.wait(1)
    end

    self._current_activity = self._first_activity._to_do(self)
    self._current_activity = self._current_activity._to_do(self)
end

I’m trying to make it so that I can cancel the round externally without having to “return” the function at every point I want to exit at.

1 Like

Alright so after taking this into hours of consideration, I used this to find a solution

local co
local canceled = false

function new_wait(co, seconds)
	local now = tick()

	while true do
		if (tick() - now) >= seconds then return end

        -- this is the thing that stops the function in its tracks
		if canceled then coroutine.yield() end

		game:GetService("RunService").Heartbeat:Wait()
	end
end

co = coroutine.create(function()
	print("Hello")

	new_wait(co, 3)

	print("World")

	new_wait(co, 3)

	print("!")

	new_wait(co, 3)
end)

co.resume()

task.wait(2)

canceled = true

I basically have 1 variable that controls the entire thread, and the yielding of that thread is dependent on my customized yielding function.