Cas d'usage des générateurs JavaScript
Les générateurs JavaScript (function*) font partie du langage depuis ES2015, pourtant de nombreux développeurs frontend continuent d’utiliser des tableaux ou des chaînes de promesses alors que les générateurs seraient plus adaptés. Leur valeur principale ne réside pas dans la performance brute — mais dans l’évaluation paresseuse, la composabilité et le contrôle précis de l’itération. Cet article explore les situations où les générateurs trouvent véritablement leur place dans le code frontend moderne.
Points clés à retenir
- Les générateurs produisent des valeurs à la demande grâce à l’évaluation paresseuse, évitant ainsi les calculs inutiles et les tableaux intermédiaires
- L’API Iterator Helpers apporte des méthodes natives
map,filterettakeaux itérateurs retournés par les générateurs, éliminant le besoin de fonctions utilitaires personnalisées yield*rend le parcours récursif d’arbres et de graphes à la fois lisible et paresseux- Les générateurs asynchrones (
async function*) s’associent àfor await...ofpour gérer la récupération de données paginées ou par lots avec une gestion d’état minimale
Ce qui différencie les générateurs JavaScript
Une fonction génératrice retourne un itérateur. L’appel de la fonction n’exécute aucun code — elle vous fournit un objet avec une méthode .next(). Chaque appel à .next() exécute le corps de la fonction jusqu’au prochain yield, puis se met en pause, préservant l’état local entre les appels.
function* range(start, end) {
for (let i = start; i < end; i++) yield i
}
for (const n of range(0, 5)) {
console.log(n) // 0, 1, 2, 3, 4
}
Parce que les générateurs implémentent le protocole d’itération, ils fonctionnent directement avec for...of, la syntaxe de décomposition et la déstructuration — aucun adaptateur nécessaire.
Itération paresseuse en JavaScript : traiter les données sans les matérialiser
La raison principale d’utiliser un générateur plutôt qu’un tableau est l’itération paresseuse : les valeurs ne sont produites que lorsqu’elles sont demandées. Cela importe lorsque :
- L’ensemble de données complet est volumineux et vous n’en avez besoin que d’une partie
- Le calcul de chaque valeur est coûteux
- La séquence est conceptuellement infinie
function* naturals() {
let n = 0
while (true) yield n++
}
// Ne calcule les valeurs que jusqu'au point d'arrêt
for (const n of naturals()) {
if (n > 100) break
}
Aucun tableau intermédiaire n’est créé. Aucune valeur au-delà du point d’arrêt n’est calculée.
L’API Iterator Helpers : pipelines paresseux intégrés
Écrire des utilitaires personnalisés map, filter et take était auparavant du code répétitif nécessaire. L’API Iterator Helpers — désormais disponible dans tous les navigateurs modernes — ajoute ces méthodes directement aux itérateurs synchrones :
const result = naturals()
.filter(n => n % 2 === 0)
.map(n => n * n)
.take(5)
.toArray() // [0, 4, 16, 36, 64]
Chaque étape est paresseuse. .toArray() est ce qui déclenche l’évaluation. Cela rend les pipelines basés sur les générateurs nettement plus propres sans bibliothèques tierces. Notez que ces helpers s’appliquent aux itérateurs synchrones — les helpers pour itérateurs asynchrones ne sont pas encore standardisés dans tous les environnements.
Discover how at OpenReplay.com.
Parcours d’arbres et de graphes
Les générateurs sont naturellement adaptés au parcours de structures récursives. Le parcours en profondeur d’un arbre de type DOM devient simple :
function* walkTree(node) {
yield node
for (const child of node.children ?? []) {
yield* walkTree(child)
}
}
for (const node of walkTree(rootNode)) {
if (node.type === 'input') processInput(node)
}
yield* délègue à un générateur imbriqué, gardant la récursion lisible et le parcours paresseux — vous vous arrêtez dès que vous trouvez ce dont vous avez besoin.
Générateurs asynchrones en JavaScript : récupération de données paginées et par lots
async function* étend le pattern aux séquences asynchrones. Combiné avec for await...of, il est bien adapté aux réponses d’API paginées :
async function* fetchPages(url) {
let nextUrl = url
while (nextUrl) {
const res = await fetch(nextUrl)
const data = await res.json()
yield data.items
nextUrl = data.nextPage ?? null
}
}
for await (const batch of fetchPages('/api/records')) {
processBatch(batch)
}
Chaque page n’est récupérée que lorsque la boucle avance. Il n’est pas nécessaire de collecter toutes les pages à l’avance ou de gérer l’état de pagination en externe — le générateur le conserve.
Quand ne pas utiliser les générateurs
Les générateurs ajoutent un niveau d’indirection. Pour une simple transformation de tableau que vous consommerez entièrement, les méthodes de tableau chaînées sont plus claires. Utilisez les générateurs lorsque la séquence est volumineuse, potentiellement infinie, ou lorsque vous devez vous arrêter tôt sans calcul gaspillé.
Conclusion
Les générateurs JavaScript excellent dans trois domaines : l’itération paresseuse sur des séquences volumineuses ou infinies, les pipelines de données composables (en particulier avec l’API Iterator Helpers), et la récupération de données asynchrones où vous avez besoin d’un contrôle séquentiel et avec état. Ils ne remplacent pas les tableaux ou async/await — ils sont l’outil approprié lorsque vous devez produire des valeurs à la demande plutôt que toutes à la fois.
FAQ
Oui. Les générateurs fonctionnent bien pour produire des séquences de données que les composants React consomment. Vous pouvez appeler un générateur à l'intérieur d'un hook useEffect ou useMemo pour calculer paresseusement des valeurs. Cependant, n'utilisez pas un générateur comme fonction de composant elle-même — React s'attend à ce que les composants retournent du JSX, pas des itérateurs.
Le générateur reste en pause à son dernier point yield. Il devient éligible au ramasse-miettes une fois qu'aucune référence à son objet itérateur ne subsiste. Si vous avez besoin d'une logique de nettoyage qui s'exécute lorsque l'itération s'arrête prématurément, enveloppez le yield dans un bloc try-finally. Le bloc finally s'exécute lorsque la méthode return de l'itérateur est appelée ou que le générateur est collecté par le ramasse-miettes.
Pour de petites collections entièrement consommées, les générateurs ont une légère surcharge due au mécanisme de pause et de reprise. La différence de performance est négligeable dans la plupart des applications. Les générateurs deviennent plus rapides en pratique lorsque vous traitez partiellement de grands ensembles de données, car ils évitent d'allouer des tableaux intermédiaires et sautent le calcul des valeurs que vous ne demandez jamais.
Un générateur asynchrone yield des valeurs progressivement au fur et à mesure qu'elles deviennent disponibles, tandis qu'une approche basée sur les Promise attend que toutes les données soient collectées avant de retourner. Cela signifie que les générateurs asynchrones vous permettent de commencer à traiter le premier lot de résultats immédiatement, de réduire l'utilisation maximale de la mémoire et de vous donner un contrôle plus fin sur le moment où chaque récupération suivante se produit.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.