Main-Thread-Scheduling
github.com
github.com
One thing that might be improved is removing the `Promise.resolve()` that `yieldOrContinue` returns when it is not time to yield. Awaiting non-Promises is totally fine: you just get that value back (the `await` does nothing). Avoiding constructing a resolved promise can help reduce the number of objects that get allocated, especially if you're in a loop doing a lot of work.
A little thing but a great improvement. I'm now wondering why I did that.
Besides, there is a JavaScript language feature that can give you finer control on routine suspension and resumption, decoupled from any of the microtask or macrotask queue. It's called the generator function. There are some good coroutine libraries based on generator functions, e.g. co.js[3] and redux-saga[4]. You can easily make something similar that can resume the suspended routine according to your scheduling policy that prioritize main thread rendering.
[1]: https://tc39.es/ecma262/#sec-promise-executor [2]: https://v8.dev/blog/fast-async#await-under-the-hood [3]: https://github.com/tj/co [4]: https://redux-saga.js.org
About the generations, do you imagine a function that accepts a generator function and the user uses yield? Something like:
``` mainThreadScheduling(function *(yieldOrContinue) { yield yieldOrContinue; }) ```
await yieldOrContinue('user-visible');
doSomeWork();
against if (isTimeToYield('user-visible')) {
await yieldControl('user-visible');
}
doSomeWork();
A few years back when I measured it on a fast laptop the cost was ~1µs to do `await 0;`And of course in a browser, everything is basically happening asynchronously. So, there are a lot of places where co-routines can be useful. And you can just spin up stuff with launch {} as well and have things running in the background. Nice with web sockets for example.
That was tried, and often failed, for many years in the OS space.
React's concurrent mode is the same thing as well.