chrisblakley / Nebula

Nebula is a WordPress theme framework that focuses on enhancing development. The core features of Nebula make it a powerful tool for designing, developing, and analyzing WordPress websites consistently, yet its deliberately uncomplicated code syntax also serves as a learning resource for programmers themselves.
https://nebula.gearside.com
GNU General Public License v2.0
141 stars 36 forks source link

Use Yield within the Scheduler API to break up long JavaScript tasks [🟥 awaiting full support] #2295

Open chrisblakley opened 3 weeks ago

chrisblakley commented 3 weeks ago

https://developer.chrome.com/blog/introducing-scheduler-yield-origin-trial

As of Chrome 129, functions can yield back to the main thread by using await scheduler.yield();

Google recommends using it liberally throughout the codebase, but I want to start with just the biggest functionality first.

Edit: Now on CanIUse: https://caniuse.com/mdn-api_scheduler_yield

chrisblakley commented 3 weeks ago

Quick notes, the function that this is within must be an async function and if scheduler.yield is not a function the JavaScript will error. So this cannot be implemented until full browser support– because I don't want to add a bunch of these throughout Nebula and then have to remove them all later...

if ( "scheduler" in window && "yield" in scheduler ){
    await scheduler.yield();
}
chrisblakley commented 3 weeks ago

Great article on this: https://www.debugbear.com/blog/scheduler-yield

chrisblakley commented 3 weeks ago

Added comments throughout Nebula for where I intend to place these when available.

//@todo "Nebula" 0: Scheduler Yield here when fully supported

These are placed in functions that are already async as well as functions that are not. Would need to test if changing non-async functions into async (and enabling the yield) breaks any functionality.

chrisblakley commented 3 weeks ago

Could use this function in the meantime...

//A function for shimming scheduler.yield with no fallback:
function yieldToMain () {
    //Use scheduler.yield if it exists
    if ( 'scheduler' in window && 'yield' in scheduler ){
        return scheduler.yield();
    }

    return; //Fall back to nothing (no yielding)
}

// Example usage:
async function doWork(){
    // Do some work:
    // ...

    await yieldToMain();

    // Do some other work:
    // ...
}
chrisblakley commented 3 weeks ago

Ok I have grown on the idea of using a shim function in the meantime while we wait for browser support.

//Yield back to the main thread when supported by the browser. Otherwise, do nothing.
nebula.yield = function(){
    //Use scheduler.yield if it exists
    if ( 'scheduler' in window && 'yield' in scheduler ){
        return scheduler.yield();
    }

    return; //Fall back to nothing (no yielding)
};

Use it like this:

await nebula.yield();

This is now used in several places throughout Nebula and can also be used in the child theme.

Just remember that the function needs to be async which means that it returns a promise! Anything calling an async function must expect a promise (and not a specific value) returned. So they can either use .then() on the function or await it. Otherwise, avoid trying to yield in that function.

chrisblakley commented 2 weeks ago

I also wrote a nebula.each() function that can be awaited to avoid race conditions while preserving the flexibility and convenience of jQuery.each().

https://nebula.gearside.com/functions/each/

chrisblakley commented 1 week ago

https://caniuse.com/mdn-api_scheduler_yield

Screenshot 2024-09-30 at 2 07 59 PM