14 methoden om JavaScript uit te stellen of in te plannen
Leer hoe je JavaScript kunt uitstellen en inplannen

Waarom JavaScript uitstellen of inplannen?
JavaScript kan (en zal) je website op een paar manieren vertragen. Dit kan allerlei negatieve gevolgen hebben voor de Core Web Vitals. JavaScript kan concurreren om netwerkbronnen, het kan concurreren om CPU bronnen (de main thread blokkeren) en het kan zelfs het parsen van een webpagina blokkeren. Het uitstellen en inplannen van je scripts kan de Core Web Vitals drastisch verbeteren.
Table of Contents!
- Waarom JavaScript uitstellen of inplannen?
- Hoe kan JavaScript timing de Core Web Vitals beïnvloeden?
- Hoe kies je de juiste uitstelmethode?
- Methode 1: Gebruik het defer attribuut
- Methode 2: Gebruik het async attribuut
- Methode 3: Gebruik modules
- Methode 4: Plaats scripts onderaan de pagina
- Methode 5: Injecteer scripts
- Methode 6: Injecteer scripts op een later tijdstip
- Methode 7: Verander het scripttype (en verander het dan terug)
- Methode 9: Gebruik readystatechange
- Methode 10: Gebruik setTimeout zonder timeout
- Methode 11: Gebruik setTimeout met een timeout
- Methode 12 Gebruik een promise om een microtaak in te stellen
- Methode 13 Gebruik een microtaak
- Methode 15: Gebruik postTask
Om de vervelende effecten die JavaScript kan hebben op de Core Web Vitals te minimaliseren, is het meestal een goed idee om op te geven wanneer een script in de wachtrij wordt geplaatst om te downloaden en te plannen wanneer het CPU-tijd in beslag kan nemen en de main thread kan blokkeren.
Hoe kan JavaScript timing de Core Web Vitals beïnvloeden?
Hoe kan JavaScript timing de Core Web Vitals beïnvloeden? Kijk maar eens naar dit real-life voorbeeld. De eerste pagina wordt geladen met 'render blocking' JavaScript. De paint metrics en de Time to interactive zijn behoorlijk slecht. Het tweede voorbeeld is van exact dezelfde pagina maar met de JavaScript uitgesteld. Je zult zien dat de LCP afbeelding nog steeds een grote klap heeft gekregen. Het derde voorbeeld heeft dezelfde script uitgevoerd na het 'load event' van de pagina en heeft de functieaanroepen opgesplitst in kleinere stukken. Deze laatste slaagt met vlag en wimpel voor de Core Web Vitals.



Standaard zal externe JavaScript in de head van de pagina de creatie van de render
tree blokkeren. Meer specifiek: wanneer de browser een script in het document tegenkomt, moet hij
de DOM-constructie pauzeren, de controle overdragen aan de JavaScript runtime, en het script laten
uitvoeren voordat hij verdergaat met de DOM-constructie. Dit zal je Paint Metrics beïnvloeden (Largest
Contentful Paint en First Contentful Paint).
Uitgestelde (deferred) of async JavaScript kan nog steeds impact hebben op paint metrics, vooral de Largest Contentful Paint omdat het zal uitvoeren, en de main thread blokkeren, zodra de DOM is gecreëerd (en veelvoorkomende LCP elementen zoals afbeeldingen misschien nog niet zijn gedownload).
Externe JavaScript-bestanden zullen ook concurreren om netwerkbronnen. Externe JavaScript-bestanden worden meestal eerder gedownload dan afbeeldingen. ALS je te veel scripts downloadt, zal het downloaden van je afbeeldingen worden vertraagd.
Last but not least, JavaScript kan gebruikersinteractie blokkeren of vertragen. Wanneer een script CPU-bronnen gebruikt (de main thread blokkeert) zal een browser niet reageren op invoer (klikken, scrollen etc) totdat dat script is voltooid.
Hoe repareert het inplannen of uitstellen van JavaScript de core web vitals?
Hoe kies je de juiste uitstelmethode?
Niet alle scripts zijn hetzelfde en elk script heeft zijn eigen functionaliteit. Sommige scripts zijn belangrijk om vroeg in het renderingproces te hebben, andere niet.

Ik vind het leuk om JavaScripts in 4 groepen in te delen op basis van hun niveau van belangrijkheid.
1. Render kritiek. Dit zijn de scripts die het uiterlijk van een webpagina zullen veranderen.
Als
ze niet laden, zal de pagina er niet compleet uitzien. Deze scripts moeten koste wat het kost worden vermeden.
Als
je ze om de een of andere reden niet kunt vermijden, mogen ze niet worden uitgesteld. Bijvoorbeeld een top slider of
een
A/B testing script.
2. Kritiek. Deze scripts zullen het uiterlijk van een
webpagina niet veranderen
(te veel) maar de pagina zal niet goed werken zonder hen. Deze scripts moeten worden uitgesteld (defer) of
asynchroon (async) worden geladen.
Bijvoorbeeld je menu scripts.
3. Belangrijk. Dit zijn scripts die je wilt laden
omdat ze waardevol zijn voor jou of de bezoeker. Ik heb de neiging om deze scripts te laden nadat het load
event
is afgevuurd. Bijvoorbeeld analytics of een 'terug naar boven' knop.
4. Nice to have.
Dit zijn
scripts waar je zonder kunt leven als het echt moet. Ik laad deze scripts met de
laagste prioriteit en voer ze alleen uit wanneer de browser inactief is. Bijvoorbeeld een chat widget of
een
facebook knop.
Methode 1: Gebruik het defer attribuut
Scripts met het defer attribuut zullen parallel downloaden en worden toegevoegd aan de defer JavaScript wachtrij. Net voordat de browser het DOMContentLoaded event zal afvuren, zullen alle scripts in die wachtrij uitvoeren in de volgorde waarin ze in het document verschijnen.
<script defer src='javascript.js'></script>
De 'defer truc' lost meestal veel problemen op, vooral de paint metrics. Helaas is er geen garantie, het hangt af van de kwaliteit van de scripts. Uitgestelde scripts zullen uitvoeren zodra alle scripts zijn geladen en de HTML is geparsed (DOMContentLoaded). Het LCP element (meestal een grote afbeelding) is tegen die tijd misschien nog niet geladen en de uitgestelde scripts zullen nog steeds een vertraging in de LCP veroorzaken.
Wanneer te gebruiken:
Gebruik uitgestelde scripts voor Kritieke scripts die zo snel mogelijk nodig zijn.
Voordelen:
- Uitgestelde scripts zullen parallel downloaden
- De DOM zal beschikbaar zijn op het moment van uitvoering
Nadelen:
- Uitgestelde scripts kunnen je LCP metrics vertragen
- Uitgestelde scripts zullen de main thread blokkeren zodra ze worden uitgevoerd
- Het is misschien niet veilig om scripts uit te stellen wanneer inline of async scripts ervan afhankelijk zijn
Methode 2: Gebruik het async attribuut
Scripts met het async attribuut downloaden parallel en zullen onmiddellijk uitvoeren nadat ze klaar zijn met downloaden.
<script async src='javascript.js'></script>
Async scripts zullen weinig doen om je pagespeed problemen op te lossen. Het is geweldig dat ze worden gedownload in parallel maar dat is het wel zo'n beetje. Zodra ze zijn gedownload, zullen ze de main thread blokkeren terwijl ze worden uitgevoerd.
Wanneer te gebruiken:
Gebruik async scripts voor Kritieke scripts die zo snel mogelijk nodig zijn en op zichzelf staand zijn (niet afhankelijk van andere scripts).
Voordelen:
- Async scripts zullen parallel downloaden.
- Async scripts zullen zo snel mogelijk uitvoeren.
Nadelen:
- DOMContentLoaded kan zowel voor als na async gebeuren.
- De uitvoeringsvolgorde van het script zal vooraf onbekend zijn.
- Je kunt geen async scripts gebruiken die afhankelijk zijn van andere async of uitgestelde scripts
Methode 3: Gebruik modules
Modulaire scripts worden standaard uitgesteld tenzij ze het async attribuut hebben. In dat geval zullen ze worden behandeld als async scripts
<script module src='javascript.js'></script>
Modules zijn een nieuwe manier van denken over JavaScript en lossen enkele ontwerpfouten op. Verder zal het gebruik van script modules je website niet versnellen.
Wanneer te gebruiken:
Wanneer je applicatie op een modulaire manier is gebouwd, is het logisch om ook JavaScript modules te gebruiken.
Voordelen:
- Modules worden standaard uitgesteld
- Modules zijn gemakkelijker te onderhouden en werken geweldig met modulair webdesign
- Modules maken eenvoudige code splitting mogelijk met dynamische imports waarbij je alleen de modules importeert die je op een bepaald moment nodig hebt.
Nadelen:
- Modules zelf zullen de Core Web Vitals niet verbeteren
- Het importeren van modules just-in-time of on the fly kan traag zijn en de FID en INP verergeren
Methode 4: Plaats scripts onderaan de pagina
Footer scripts worden op een later tijdstip in de wachtrij geplaatst voor download. Dit zal andere resources prioriteren die boven de script tag in het document staan.
<html>
<head></head>
<body>
[uw pagina inhoud hier]
<script defer src='javascript.js'></script>
</body>
</html>
Het plaatsen van je scripts onderaan de pagina is een interessante techniek. Dit zal andere resources (zoals afbeeldingen) inplannen vóór je scripts. Dit zal de kans vergroten dat ze beschikbaar zijn voor de browser en op het scherm worden geschilderd voordat de JavaScript-bestanden klaar zijn met downloaden en de main thread zal worden geblokkeerd door scriptuitvoering. Nog steeds ... geen garantie.
Wanneer te gebruiken:
Wanneer je scripts al behoorlijk goed presteren maar je andere resources zoals afbeeldingen en webfonts iets wilt prioriteren.
Voordelen:
- Het plaatsen van scripts onderaan de pagina vereist niet veel kennis.
- Indien correct gedaan is er geen risico dat dit je pagina zal breken
Nadelen:
- Kritieke scripts kunnen later worden gedownload en uitgevoerd
- Het lost geen onderliggende JavaScript-problemen op
Methode 5: Injecteer scripts
Geïnjecteerde scripts worden behandeld als async scripts. Ze worden parallel gedownload en worden onmiddellijk na het downloaden uitgevoerd.
<script>
const loadScript = (scriptSource) => {
const script = document.createElement('script');
script.src = scriptSource;
document.head.appendChild(script);
}
// roep de loadscript functie aan die 'javascript.js' injecteert
loadScript('javascript.js');
</script>
Vanuit een Core Web Vitals perspectief is deze techniek precies hetzelfde als het gebruik van <script async>.
Wanneer te gebruiken:
Deze methode wordt vaak gebruikt door scripts van derden die zo vroeg mogelijk triggeren. De functieaanroep maakt het gemakkelijk om code in te sluiten en te comprimeren.
Voordelen:
- Op zichzelf staande code die een async script injecteert.
Nadelen:
- DOMContentLoaded kan zowel voor als na het laden van het script gebeuren.
- De uitvoeringsvolgorde van het script zal vooraf onbekend zijn.
- Je kunt dit niet gebruiken op scripts die afhankelijk zijn van andere async of uitgestelde scripts
Methode 6: Injecteer scripts op een later tijdstip
Nice-to-have scripts mogen naar mijn mening nooit uitgesteld worden geladen. Ze moeten worden geïnjecteerd op het meest geschikte moment. In het onderstaande voorbeeld zal het script uitvoeren nadat de browser het 'load' event heeft verzonden.
<script>
window.addEventListener('load', function () {
// zie methode 5 voor de loadscript functie
loadScript('javascript.js');
});
</script>
Dit is de eerste techniek die de Largest Contentful Paint betrouwbaar zal verbeteren. Alle belangrijke resources, inclusief afbeeldingen, worden gedownload wanneer de browser het 'load event' afvuurt. Dit kan allerlei problemen introduceren omdat het lang kan duren voordat het load event wordt aangeroepen.
Wanneer te gebruiken:
Voor nice-to-have scripts die geen reden hebben om de paint metrics te beïnvloeden.
Voordelen:
- Zal niet concurreren om kritieke resources omdat het het script zal injecteren zodra de pagina en de resources zijn geladen
Nadelen:
- Als je pagina slecht is ontworpen qua Core Web Vitals kan het lang duren voordat de pagina het 'load' event verstuurt
- Je moet voorzichtig zijn om dit niet toe te passen op kritieke scripts (zoals lazy loading, menu etc)
Methode 7: Verander het scripttype (en verander het dan terug)
Als er ergens op de pagina een script tag wordt gevonden die 1. een type attribuut heeft en 2. het type attribuut is niet "text/javascript", dan zal het script niet worden gedownload en uitgevoerd door een browser. Veel JavaScript Loaders (zoals CloudFlare's RocketLoader) vertrouwen op dit principe. Het idee is vrij simpel en elegant.
Eerst worden alle scripts herschreven als dit:
<script type="some-cool-script-type" src="javascript.js"></script>
Vervolgens worden deze scripts op een bepaald moment tijdens het laadproces terug geconverteerd naar 'normale javascripts'.
Wanneer te gebruiken:
Dit is geen methode die ik zou aanraden. Het repareren van JavaScript impact zal veel meer vergen dan alleen elk script een beetje verder in de wachtrij te plaatsen. Aan de andere kant, als je weinig controle hebt over de scripts die op de pagina draaien of onvoldoende JavaScript kennis hebt, is dit misschien je beste gok.
Voordelen:
- Het is gemakkelijk, schakel gewoon rocket loader of een andere plugin in en al je scripts worden nu uitgevoerd op een iets later tijdstip.
- HET zal waarschijnlijk je paint metrics repareren mits je geen JS gebaseerde lazy loading hebt gebruikt.
- Het zal werken voor inline en externe scripts.
Nadelen:
- Je zult geen fijnmazige controle hebben over wanneer de scripts uitvoeren
- Slecht geschreven script kan breken
- Het gebruikt JavaScript om JavaScript te repareren
- Het doet niets om langlopende scripts te repareren
Methode 8: Gebruik de intersection observer
Met de intersection observer kun je een functie uitvoeren (die in dit geval een externe JavaScript laadt) wanneer een element in de zichtbare viewport scrolt.
<script>
const handleIntersection = (entries, observer) => {
if (entries?.[0].isIntersecting) {
// laad je script of voer een andere
functie uit zoals het triggeren van een lazy loaded element
loadScript('javascript.js');
// verwijder de observer
observer.unobserve(entries?.[0].target);
}
};
const Observer = new window.IntersectionObserver()
Observer.observe(document.querySelector('footer'));
</script>
Dit is verreweg de meest effectieve methode om JavaScript uit te stellen die er is. Laad alleen de scripts die je nodig hebt, net voordat je ze nodig hebt. Helaas is het echte leven bijna nooit zo schoon en niet veel scripts kunnen worden gekoppeld aan een element dat in beeld scrolt.
Wanneer te gebruiken:
Gebruik deze techniek zoveel mogelijk! Wanneer een script alleen interactie heeft met een off-screen component (zoals een kaart, een slider, een formulier) is dit de beste manier om dit script te injecteren.
Voordelen:
- Zal niet interfereren met Core Web Vitals LCP en FCP
- Zal nooit scripts injecteren die niet worden gebruikt. Dit zal de FID en INP verbeteren
Nadelen:
- Mag niet worden gebruikt met componenten die in de zichtbare viewport kunnen zijn
- Is moeilijker te onderhouden dan basis <script src="...">
- Kan een layout shift introduceren
Methode 9: Gebruik readystatechange
document.readystate kan worden gebruikt als en alternatief voor het 'DOMContentloaded' en 'load' event. De
'interactive' readystate is meestal een goede plek om kritieke scripts aan te roepen die de
DOM moeten veranderen of event handlers moeten toevoegen.
De 'complete' ready state is een goede plek om scripts aan te roepen
die minder kritiek zijn.
document.addEventListener('readystatechange', (event) => {
if (event.target.readyState === 'interactive') {
initLoader();
} else if (event.target.readyState === 'complete') {
initApp();
}
});
Methode 10: Gebruik setTimeout zonder timeout
setTimeout is een afgekeurde maar zwaar onderschatte methode in de pagespeed gemeenschap. setTimeout heeft een slechte naam gekregen omdat het vaak wordt misbruikt. Veel ontwikkelaars geloven dat setTimeout alleen kan worden gebruikt om scriptuitvoering te vertragen met de ingestelde hoeveelheid milliseconden. Hoewel dit waar is, doet setTimeout eigenlijk iets veel interessanters. Het creëert een nieuwe taak aan het einde van de browser event loop. Dit gedrag kan worden gebruikt om je taken effectief in te plannen. Het kan ook worden gebruikt om lange taken op te splitsen in afzonderlijke kleinere taken
<script>
setTimeout(() => {
// laad een script of voer een andere functie uit
console.log('- Ik word aangeroepen vanuit een 0ms timeOut()')
}, 0);
console.log('- Ik was de laatste in de rij maar werd als eerste uitgevoerd')
/*
Output:
- Ik was de laatste in de rij maar werd als eerste uitgevoerd
- Ik word aangeroepen vanuit een 0ms timeOut()
*/
</script>
Wanneer te gebruiken:
setTimeout creëerde een nieuwe taak in de browsers event loop. Gebruik dit wanneer je main thread wordt geblokkeerd door vele functieaanroepen die sequentieel draaien.
Voordelen:
- Kan langlopende code opsplitsen in kleinere stukken.
Nadelen:
- setTimeout is een nogal ruwe methode en biedt geen prioriteitstelling voor belangrijke scripts.
- Zal de uit te voeren code toevoegen aan het einde van de loop
Methode 11: Gebruik setTimeout met een timeout
Dingen worden nog interessanter wanneer we setTimeout aanroepen met een timeout van meer dan 0ms
<script>
setTimeout(() => {
// laad een script of voer een andere functie uit
console.log('- Ik word aangeroepen vanuit een 10ms timeOut()')
}, 10);
setTimeout(() => {
// laad een script of voer een andere functie uit
console.log('- Ik word aangeroepen vanuit een 0ms timeOut()')
}, 0);
console.log('- Ik was de laatste in de rij maar werd als eerste uitgevoerd')
/*
Output:
- Ik was de laatste in de rij maar werd als eerste uitgevoerd
- Ik word aangeroepen vanuit een 0ms timeOut()
- Ik word aangeroepen vanuit een 10ms timeOut()
*/
</script>
Wanneer te gebruiken:
Wanneer je een gemakkelijke methode nodig hebt om het ene script na het andere in te plannen, zal een kleine timeout de klus klaren
Voordelen:
- Ondersteund op alle browsers
Nadelen:
- Biedt geen geavanceerde planning
Methode 12 Gebruik een promise om een microtaak in te stellen
Het creëren van een micro-taak is ook een interessante manier om JavaScript in te plannen. Micro-taken worden ingepland voor uitvoering onmiddellijk nadat de huidige uitvoeringsloop is voltooid.
<script>
const myPromise = new Promise((resolve, reject) => {
resolve();
}).then(
() => {
console.log('- Ik was ingepland na een promise')
}
);
console.log('- Ik was de laatste in de rij maar werd als eerste uitgevoerd')
/*
Output:
- Ik was de laatste in de rij maar werd als eerste uitgevoerd
- Ik was ingepland na een promise
*/
</script>
Wanneer te gebruiken:
Wanneer een taak onmiddellijk na een andere taak moet worden ingepland.
Voordelen:
- De microtaak zal onmiddellijk worden ingepland nadat de taak klaar is met draaien.
- Een microtaak kan worden gebruikt om minder belangrijke stukjes JavaScript code in hetzelfde event te vertragen.
Nadelen:
- Zal de main thread input niet opsplitsen in een kleiner deel. De browser zal geen kans hebben om te reageren op gebruikersinvoer.
- Je zult waarschijnlijk nooit microtaken hoeven te gebruiken om de Core Web Vitals te verbeteren tenzij je al precies weet wat je doet.
Methode 13 Gebruik een microtaak
Hetzelfde resultaat kan worden bereikt door queueMicrotask() te gebruiken. Het voordeel van het gebruik van queueMicrotask() boven een promise is dat het iets sneller is en je je promises niet hoeft af te handelen.
<script>
queueMicrotask(() => {
console.log('- Ik ben een microtaak')
})
console.log('- Ik was de laatste in de rij maar werd als eerste uitgevoerd')
/*
Output:
- Ik was de laatste in de rij maar werd als eerste uitgevoerd
- Ik ben een microtaak
*/
</script>
Methode 14: Gebruik requestIdleCallback
De window.requestIdleCallback() methode zet een functie in de wachtrij om te worden aangeroepen tijdens inactieve periodes van een browser. Dit stelt ontwikkelaars in staat om achtergrond en lage prioriteit werk op de main event loop uit te voeren, zonder latency-kritieke events zoals animatie en invoerrespons te beïnvloeden. Functies worden over het algemeen aangeroepen in first-in-first-out volgorde; echter, callbacks die een timeout gespecificeerd hebben kunnen indien nodig buiten de volgorde worden aangeroepen om ze uit te voeren voordat de timeout verstrijkt.
<script>
requestIdleCallback(() => {
const script = document.createElement('script');
script.src = 'javascript.js';
document.head.appendChild(script);
});
</script>
Wanneer te gebruiken:
Gebruik dit voor scripts die Nice to have zijn of om niet-kritieke taken na gebruikersinvoer af te handelen
Voordelen:
- JavaScript uitvoeren met minimale impact voor de gebruiker
- Zal hoogstwaarschijnlijk FID en INP verbeteren
Nadelen:
- Ondersteund in de meeste browsers maar niet allemaal. Er zijn poly-fills die terugvallen op setTimeout();
- Geen garantie dat de code ooit zal vuren
Methode 15: Gebruik postTask
De methode stelt gebruikers in staat om optioneel een minimale vertraging op te geven voordat de taak zal draaien, een prioriteit voor de taak, en een signaal dat kan worden gebruikt om de taakprioriteit te wijzigen en/of de taak af te breken. Het retourneert een promise die wordt opgelost met het resultaat van de taak callback functie, of afgewezen met de reden voor afbreken of een fout die in de taak is gegooid.
<script>
scheduler.postTask(() => {
const script = document.createElement('script');
script.src = 'javascript.js';
document.head.appendChild(script);
}, { priority: 'background' });
</script>
Wanneer te gebruiken:
postTask is de perfecte API om scripts mee in te plannen. Helaas is de browserondersteuning op dit moment slecht dus je zou het niet moeten gebruiken.
Voordelen:
- Volledige controle over JavaScript uitvoeringsplanning!
Nadelen:
- Niet ondersteund in sommige belangrijke browsers.
I have done this before at your scale.
Complex platforms, large dev teams, legacy code. I join your team as a specialist, run the performance track, and hand it back in a state you can maintain.
Discuss Your Situation
