Microtasks, macrotasks, generators, Proxy, WeakMap
setState est async,
pourquoi un useEffect se déclenche après le render, ou pourquoi tes tests "passent" mais ton UI "lag".
JS est mono-thread : une seule pile d'exécution (Call Stack).
console.log("1 — synchrone");
setTimeout(() => console.log("2 — macrotask (setTimeout)"), 0);
Promise.resolve().then(() => console.log("3 — microtask (Promise)"));
console.log("4 — synchrone");
// Ordre d'affichage :
// 1 — synchrone
// 4 — synchrone
// 3 — microtask (Promise) ← avant setTimeout même avec 0ms !
// 2 — macrotask (setTimeout)
Microtasks (prioritaires) : .then(), async/await, queueMicrotask()
Macrotasks (après) : setTimeout, setInterval, fetch callbacks, événements DOM
// Croire que setTimeout(fn, 0) s'exécute immédiatement
setTimeout(() => console.log("immédiat ?"), 0);
console.log("ceci s'affiche avant !");
Un generator est une fonction qui peut s'interrompre et reprendre.
function* compteur() {
yield 1; // s'arrête ici, retourne 1
yield 2;
yield 3;
}
const gen = compteur();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
// Utilisable avec for...of
for (const n of compteur()) {
console.log(n); // 1, 2, 3
}
Cas concret — pagination infinie :
function* paginer(tableau, taillePage) {
for (let i = 0; i < tableau.length; i += taillePage) {
yield tableau.slice(i, i + taillePage);
}
}
const data = [1,2,3,4,5,6,7,8,9,10];
for (const page of paginer(data, 3)) {
console.log("Page :", page); // [1,2,3], [4,5,6], [7,8,9], [10]
}
Comme Map et Set mais les références sont faibles — pas de fuite mémoire.
// Map classique → l'objet reste en mémoire même si plus référencé ailleurs
const map = new Map();
let obj = { nom: "Alice" };
map.set(obj, "données");
obj = null; // obj.nom reste en mémoire car map le retient !
// WeakMap → se nettoie automatiquement quand l'objet n'existe plus
const weak = new WeakMap();
let obj2 = { nom: "Bob" };
weak.set(obj2, "données");
obj2 = null; // garbage collected automatiquement
Un Proxy enveloppe un objet et intercepte ses opérations.
const handler = {
get(cible, propriete) {
console.log(`Accès à : ${propriete}`);
return cible[propriete];
},
set(cible, propriete, valeur) {
if (typeof valeur !== 'number') throw new TypeError("Seulement des nombres !");
cible[propriete] = valeur;
return true;
}
};
const scores = new Proxy({}, handler);
scores.alice = 95; // OK
scores.bob = "nul"; // TypeError !
console.log(scores.alice); // "Accès à : alice" puis 95
reactive()).// Copie profonde native (sans JSON.stringify/parse)
const original = { nom: "Alice", date: new Date(), tab: [1, 2, 3] };
const copie = structuredClone(original);
copie.tab.push(4);
console.log(original.tab); // [1, 2, 3] — non modifié
| Concept | À retenir |
|---|---|
| Event Loop | Call Stack vide → prend une tâche |
| Microtask | Promises — priorité max |
| Macrotask | setTimeout — après les microtasks |
| Generator | function* + yield — pause/reprise |
| WeakMap | Map sans fuite mémoire |
| Proxy | intercepte les accès à un objet |
| structuredClone | copie profonde native |
Visualise l'Event Loop en action avec un projet interactif sur les tâches asynchrones.
Télécharge les fichiers, ouvre-les dans ton éditeur et travaille directement dedans.
5 exercices sur l'Event Loop, les generators, les microtasks, Proxy et generator + async.
Corrections complètes. À consulter après avoir essayé.