Hoe je kunt yielden naar de main thread
Yield naar de main thread om de responsiviteit van je pagina te verbeteren

Yield naar de main thread
Stel je een romantische film voor. De setting is een kleine Franse markt in het centrum van een dorpje. De straten zijn gevuld met stelletjes die koffie drinken, croissants eten en bloemen kopen. Stel je nu voor wat er gebeurt als slechts 1 koper tegelijk iets kan kopen van een verkoper, terwijl alle anderen op hun beurt moeten wachten. De bakker wordt overspoeld met bestellingen, er breken ruzies uit bij de bloemist en de romantische wandeling verandert in frustrerend wachten.
Wel... dat is zo'n beetje wat er op een website gebeurt als het te druk wordt.
Het belang van Yielding
De main thread van een browser verwerkt alle belangrijke processen en plant alle belangrijke taken (het parsen van HTML en CSS, het uitvoeren van JavaScript code, het afhandelen van input events zoals clicks en scrolls, en de visuele rendering).
De main thread van een browser werkt volgens een single-threaded model. Dit betekent dat het maar één taak tegelijk kan uitvoeren. Wanneer een taak (meestal JavaScript execution of rendering) begint, voert de browser deze taak uit 'tot voltooiing' en stopt pas als deze klaar is. Dit betekent dat er geen andere taak wordt ingepland of uitgevoerd totdat de vorige taak is afgerond. Dit heet 'blocking the main thread'. Het blokkeren van de main thread is een probleem wanneer bezoekers met een pagina interageren, omdat de pagina niet reageert tijdens de blocking time.
Een manier om het blokkeren van de main thread op te lossen is door te 'yielden naar de main thread'. Yielding is een techniek waarbij lange taken worden opgesplitst in meerdere kleinere taken om de main thread de ruimte te geven om belangrijkere taken (zoals user input) af te handelen.
Long tasks en blocking period: Wanneer een taak langer duurt dan 50 milliseconden, wordt deze geclassificeerd als een long task. Alles boven die drempel van 50 milliseconden staat bekend als de 'blocking period' van de taak. Door deze long tasks op te splitsen in kleinere stukken, blijft de browser responsief, zelfs bij het verwerken van rekenintensieve operaties.
Oude yielding strategieën
Vóór de nieuwe Prioritized Task Scheduling API waren er 4 manieren om te yielden naar de main thread. Allemaal met hun eigen beperkingen en nadelen!
- setTimeout(): De meest gebruikte strategie.
setTimeout()
werkt door code uit te voeren in een callback functie na een gespecificeerde vertraging. Door een vertraging (of timeout) van 0 in te stellen, voegtsetTimeout()
de taak toe aan het einde van de wachtrij, waardoor andere taken eerst kunnen worden uitgevoerd. Een groot probleem is dat, setTimeout() niet is ontworpen voor precieze planning, omdat taken alleen naar het einde van de wachtrij kunnen worden verplaatst. - requestAnimationFrame():
requestAnimationFrame()
werkt door een functie in de wachtrij te plaatsen die wordt uitgevoerd vóór de volgende repaint van de browser.requestAnimationFrame()
wordt vaak gecombineerd metsetTimeout()
om ervoor te zorgen dat callback functies worden ingepland na de volgende layout update. - requestIdleCallback(): Deze methode is het meest geschikt voor niet-kritieke, lage-prioriteit taken die kunnen worden uitgevoerd tijdens de downtime van een browser. Hoewel het helpt om de main thread vrij te houden voor belangrijkere taken, heeft
requestIdleCallback()
een aanzienlijke beperking: er is geen garantie dat de ingeplande taken snel (of ooit!!!) zullen worden uitgevoerd, vooral niet op een drukke main thread. - isInputPending():
isInputPending()
werkt door te controleren op openstaande user inputs en yieldt alleen als er een input wordt gedetecteerd.isInputPending()
kan worden gebruikt in combinatie met andere yielding functies om onnodig yielden te voorkomen. Helaas kan het false negatives teruggeven en lost het niet de noodzaak op om te yielden voor andere performance-kritieke taken zoals animaties.
Introductie van: scheduler.yield()
De beperkingen van deze 4 methoden waren een punt van zorg voor het Chrome-team, vooral omdat veel sites faalden voor de Interaction to Next Paint metric. Om dit op te lossen en te helpen, heeft het Chrome-team nieuwe APIs gebouwd die granulaire controle geven over javascript scheduling.
Maak kennis met scheduler.yield()
! Met window.scheduler.yield()
kunnen developers onmiddellijk en effectief yielden naar de main thread, zonder de volgorde van taken te wijzigen of extra complexiteit te creëren.
Gebruik Scheduler.yield() effectief
Laten we de code induiken en ontdekken hoe je scheduler.yield() gebruikt zoals het bedoeld is! Voordat je scheduler.yield() gaat gebruiken, is het belangrijk om de browsercompatibiliteit te controleren, want het is een experimentele feature en wordt nog niet door alle browsers ondersteund.
Voorbeeldcode
function yieldToMain() {
if ('scheduler' in window && 'yield' in window.scheduler) {
return window.scheduler.yield();
}
return new Promise((resolve) => {
setTimeout(resolve, 0);
});
}
De functie yieldToMain()
is de kern van het teruggeven van controle aan de main thread. Het controleert eerst of zowel window.scheduler als window.scheduler.yield bestaan. Als dat zo is, kunnen we veilig window.scheduler.yield gebruiken. Als window.scheduler.yield niet wordt ondersteund, gebruikt de code een fallback naar setTimeout().
Kortere en production ready versie
Of gebruik deze kortere, production ready versie die de ternaire operator gebruikt:
/* of korter en production ready: */
function yieldToMain(){
return"scheduler"in window&&"yield"in window.scheduler?
window.scheduler.yield():new Promise(e=>{setTimeout(e,0)})
}
Real-life voorbeeld: Zoeken verbeteren met yieldToMain():
Laten we kijken hoe we yieldToMain()
kunnen gebruiken om de zoekervaring voor je gebruikers te verbeteren:
De functie handleSearch()
laat zien hoe yieldToMain()
effectief kan worden gebruikt. Eerst wordt de inhoud van de knop bijgewerkt om onmiddellijke feedback te geven dat er een zoekopdracht wordt uitgevoerd. Je kunt hier yieldToMain() gebruiken om de browser de layout te laten bijwerken.
Vervolgens haalt fetchData()
de zoekdata op, en updateHTML(data)
toont de resultaten. Een volgende yieldToMain()
zorgt voor een snelle layout update (je weet immers nooit zeker of er na deze taak andere event listeners wachten). Tot slot worden andere, minder belangrijke taken ingepland tijdens de idle time van de browser. Merk op dat ik hier niet naar de main thread yield, aangezien requestIdleCallback()
alleen wordt uitgevoerd wanneer de main thread idle is.
async function handleSeach(){
/* update snel de knopinhoud na het submitten*/
updateButtontoPending();
/* Yield naar de Main */
await yieldToMain();
/* haal data op en update de html*/
const data = await fetchData();
updateHTML(data);
/* Yield opnieuw naar de Main */
await yieldToMain();
/* sommige functies mogen alleen draaien tijdens browser idle time*/
requestIdleCallback(sendDataToAnalytics);
}
Waarom scheduler.yield() gewoon beter is
In tegenstelling tot setTimeout()
, dat uitgestelde taken toevoegt aan het einde van de task queue, pauzeert scheduler.yield()
slechts de javascript queue. Gepauzeerde taken blijven aan de voorkant van de queue staan, wat ervoor zorgt dat ze zo snel mogelijk worden uitgevoerd nadat taken met een hogere prioriteit (zoals het afhandelen van input callbacks) zijn voltooid. Dit "front-of-queue" gedrag pakt het grootste probleem van de oude yielding methoden aan. En developers kunnen nu 'yielden naar de main thread' zonder het risico te lopen dat hun belangrijke taken worden vertraagd door andere, minder belangrijke taken.
Een ander voordeel van scheduler.yield()
is dat het is ontworpen om samen te werken met de Prioritized Task Scheduling API zoals scheduler.postTask()
, waarmee je taken kunt prioriteren op basis van hun belangrijkheid. Deze combinatie van features geeft developers een krachtige toolkit om hun webapplicaties te optimaliseren voor responsiviteit en performance.

