t=tick()spawn(function()print(tick()-t)end)
0.01389741897583
?
waiting 0 yields 1/30th of a second. Spawning yields until the next resume at < 1/60th of a second, its expected behavior.
I hate wait(0)
as much as the next guy but this has nothing to do with the OP.
However, how this argument started is a good idea - you could avoid this error by simply doing this:
~~~
spawn(function() part:Destroy() end)
~~~
Why are we even talking about wait? The problem here is logical. If you change the parent while youâre executing an AncestryChanged event then what do you do with the connections that havenât been fired yet?
part.AncestryChanged:connect(function()
part.Parent = workspace
end)
This will error because youâre trying to change the data that fired the event while the event is still firing. wait() isnât a magic solution that makes the error not happen, it yields the thread and allows the event to continue executing so that your code will change the data after the event finishes.
If this is the reason why this happens then canât there be a check to see if any unfired connections exist? If so, finish them then destroy? If theres no connections (As in most cases) just destroy.
That would basically turn Destroy into a yieldfunction and thus changes the behaviour/expectations that you should have of the method, I donât think thatâs a good idea. Yielding yourself before calling Destroy seems a lot cleaner, then you are actively aware of the yielding, instead of it being a potential side effect.
Because no one expects Destroy to execute other lua code that changes the state of the game. What we really need is a way to manually yield the thread so that those other connections can execute.
Ah, wait()
coroutine.yield() would work better in this case. Itâs basically wait() with no resume delay.
To be specific, the code everyone is looking for here is:
coroutine.yield(coroutine.running())
Which will add the current thread back into the âexecution queueâ, but still for the current frame, to be executed after all other currently queued threads are executed. Basically, it will âwaitâ until all other threads for the current frame have completed, but not actually âwaitâ any frames (as will normally be the case if you use the âwait()â function). You would use it something like this:
game.Workspace.ChildAdded:connect(function(child)
coroutine.yield(coroutine.running())
child:Destroy()
end)
Which would successfully destroy the part without it existing for any frames.
running() isnât necessary, though. yield() already pauses the calling thread in its own, and the scheduler doesnât do anything with the arguments passed to yield().
Zeuxcg described this as a really ugly bug that shouldnât be utilized.
I wouldnât advise using this @PlaceRebuilder, as this might get patched eventually.
He meant that you at least shouldnât use it for loops, because it means you execute several times per frame until the time quantum of the Lua scheduler runs out (so you literally fill up the remainder of the time quantum with your task, several times).
Itâs not as ugly if you just call it once to put the coroutine back into the queue for the current frame temporarily. Not sure if the fact that it processes it again in the same cycle is the intended behaviour though.
This scheduling issue should be fixed btw.