(1) You can use a Lua hook to cause a bit of Lua code to run whenever there's a function call or resume, or after each line of code, or after every N bytecode instructions. Inside this hook, you can call yield. This way you can preempt running Lua code without requiring the Lua code to explicitly yield if you don't want to. (You might need to come up with some sort of serialization primitive to make sure it doesn't leave you in an inconsistent state, but that also might not be a problem depending on how the rest of the system is architected.)
Been considering that, if I can prove to myself that this `hs_yield` function isn't introducing other issues, and it has some appeal because a lot of our users aren't programmers (or weren't to begin with) beyond simple scripting, so keeping implementation details simple and hidden can be a plus.
(2) Every event loop system I know of has a way to create synthetic events. If you just want to yield to the event loop and get called right back after the pending events have been processed, just create an event that calls coroutine.resume whenever the Lua code yields.
That might be a reasonable approach if I hadn't just discovered that we have a fundamental issue with coroutines to begin with in Hammerspoon: see
https://github.com/Hammerspoon/hammerspoon/issues/2306, if you want the nitty-gritty, but it boils down to any approach with leverages coroutines will have to wait until that's fixed first...