Check if function yielded (without stopping it)

How do we write the didyield function?

--didyield(func,...args)--returns true/false denoting whether func yielded when called with args
print(didyield(wait,1))--true
print(didyield(print,'hi'))--false
print(didyield(didyield,didyield))--idk, what should it be?

What is didyield? It doesn’t seem to be a built-in global.

I want it to behave based on how the op describes it

1 Like

You could probably use xpcall to detect this. If it yields, it should spit you an error.

The function doesn’t finish executing (that’s why I say in the thread name “without stopping it”)

If you want it to return immediately (and have the called function run in a new thread) in the case where it did yield:

function didyield(func, ...)
    local evt = Instance.new("BindableEvent")
    local args = {...}
    local argCount = select("#", ...)
    local reachedEnd = false
    evt.Event:Connect(function()
        func(unpack(args, 1, argCount))
        reachedEnd = true
    end)
    evt:Fire()
    return not reachedEnd
end
2 Likes

Curious why you just didn’t use:
func(...)
or func(unpack({...}))

It might have been cleaner to write it as

local function didyield(f,...)
	local finished=false
	coroutine.wrap(function(...)
		f(...)
		finished=true
	end)(...)
	return not finished
end

on the return line I think you are supposed to return reachedEnd instead of not reachedEnd @stravant

1 Like

coroutines bad. You should use FastSpawn where possible.

Why are they bad? There are some bugs with coroutine.yield that Roblox needs to fix, but I don’t know of any with coroutine.wrap?

They swallow errors. That’s the only real reason to avoid them.
There are no performance issued between coroutine and fastspawn iirc.

“...” only exists directly within the scope of the function it’s an argument of, you can’t use the parent’s ... in the inner lambda function like you can normal local variables. You would get this just calling func(...):

11:18:37.624 - :7: Cannot use ‘…’ outside a vararg function

The reason for the arg count explicitly, is that if you pass a nil argument at the end of the argument list, unpack will lose it unless you explicitly record the count:

print(unpack({1, 2, 3, nil})) -> 1, 2, 3
print(1, 2, 3, nil) -> 1, 2, 3, nil
3 Likes

coroutine.wrap doesn’t swallow errors
and assert(coroutine.resume(t,...)) won’t swallow the errors either

Not sure if this is even useful but you can probably use tick() to count the time the function took to execture and if it’s too much (like more than a second) you return true as in it yielded.

function didyield(func)
    local start = tick()
    func()
    local finish = tick()-start
    if finish > 1 then
        return true --yielded
    else
        return false -- didn't yield
    end
    
end

Update: tested it and seems to be working

function cool ()
	wait(5)
end


print(didyield(print)) --false
print(didyield(cool)) --true

That looks more elegant, but it’s really more correct to use Roblox primitives for this. Mixing Roblox engine managed coroutines (spawn / delay / wait / yielding methods) with user managed coroutines (coroutine.create / resume / yield) is tenuous since it has had varying behavior at different points in the engine’s history. It’s pretty safe at this point with how much people use coroutine.wrap for thread spawning, but that’s why I went with the object solution instead.

There are some cases where mixing the coroutine library with Roblox engine threads has surprising “broken” looking results.

1 Like