Débuter avec les aides d'itérateurs JavaScript

Si vous avez déjà essayé de traiter un jeu de données massif en JavaScript, vous connaissez la difficulté. Les méthodes de tableau traditionnelles comme .map()
et .filter()
vous obligent à charger tout en mémoire d’un coup. Essayez cela avec un million d’enregistrements ou un flux de données infini, et votre application plante. Les aides d’itérateurs JavaScript résolvent ce problème en apportant l’évaluation paresseuse au cœur du langage.
Cet article vous montre comment utiliser les nouvelles méthodes d’aide d’itérateurs, comprendre leurs avantages en termes de performance, et les appliquer à des scénarios concrets comme le traitement de gros fichiers, la gestion de flux d’API, et le travail avec des séquences infinies.
Points clés à retenir
- Les aides d’itérateurs fournissent une évaluation paresseuse pour un traitement de données efficace en mémoire
- Convertissez les tableaux avec
.values()
et autres itérables avecIterator.from()
- Les méthodes comme
.map()
,.filter()
, et.take()
s’enchaînent sans créer de tableaux intermédiaires - Parfait pour les séquences infinies, gros fichiers, et données en flux
- Usage unique seulement - créez de nouveaux itérateurs pour plusieurs itérations
Comprendre le protocole d’itérateur
Avant de plonger dans les nouvelles aides, clarifions ce qui rend les itérateurs spéciaux. Un itérateur est simplement un objet avec une méthode .next()
qui retourne des paires {value, done}
:
const iterator = {
current: 0,
next() {
return this.current < 3
? { value: this.current++, done: false }
: { done: true }
}
}
console.log(iterator.next()) // { value: 0, done: false }
console.log(iterator.next()) // { value: 1, done: false }
Les tableaux, Sets, Maps, et générateurs implémentent tous le protocole d’itérateur via leur méthode [Symbol.iterator]()
. Ce protocole alimente les boucles for...of
et l’opérateur de décomposition, mais jusqu’à récemment, les itérateurs manquaient des méthodes de programmation fonctionnelle que les développeurs attendent.
Aides d’itérateurs JavaScript : les nouveautés
Les aides d’itérateurs étendent le prototype Iterator avec des méthodes qui reflètent les opérations de tableau mais fonctionnent de manière paresseuse :
Méthode | Description | Retourne |
---|---|---|
.map(fn) | Transforme chaque valeur | Iterator |
.filter(fn) | Produit les valeurs qui passent le test | Iterator |
.take(n) | Produit les n premières valeurs | Iterator |
.drop(n) | Ignore les n premières valeurs | Iterator |
.flatMap(fn) | Mappe et aplatit les résultats | Iterator |
.reduce(fn, init) | Agrège en une seule valeur | Value |
.find(fn) | Première valeur passant le test | Value |
.some(fn) | Teste si une valeur passe | Boolean |
.every(fn) | Teste si toutes les valeurs passent | Boolean |
.toArray() | Collecte toutes les valeurs | Array |
Pour utiliser ces méthodes, convertissez d’abord votre structure de données en itérateur :
// Pour les tableaux
const result = [1, 2, 3, 4, 5]
.values() // Convertir en itérateur
.filter(x => x % 2 === 0)
.map(x => x * 2)
.toArray() // [4, 8]
// Pour les autres itérables
const set = new Set([1, 2, 3])
const doubled = Iterator.from(set)
.map(x => x * 2)
.toArray() // [2, 4, 6]
Évaluation paresseuse vs empressée : la différence clé
Les méthodes de tableau traditionnelles traitent tout immédiatement :
// Empressée - traite tous les éléments immédiatement
const eager = [1, 2, 3, 4, 5]
.map(x => {
console.log(`Mapping ${x}`)
return x * 2
})
.filter(x => x > 5)
// Affiche : Mapping 1, 2, 3, 4, 5
// Résultat : [6, 8, 10]
Les aides d’itérateurs traitent les valeurs seulement quand elles sont consommées :
// Paresseuse - traite seulement ce qui est nécessaire
const lazy = [1, 2, 3, 4, 5]
.values()
.map(x => {
console.log(`Mapping ${x}`)
return x * 2
})
.filter(x => x > 5)
.take(2)
// Rien d'affiché encore !
const result = [...lazy]
// Affiche : Mapping 1, 2, 3
// Résultat : [6, 8]
Notez comment la version paresseuse s’arrête après avoir trouvé deux valeurs correspondantes, ne traitant jamais les éléments 4 et 5. Cette efficacité devient cruciale lors du travail avec de gros jeux de données.
Exemples pratiques et cas d’usage
Traitement de gros fichiers ligne par ligne
Au lieu de charger un fichier entier en mémoire :
async function* readLines(file) {
const reader = file.stream().getReader()
const decoder = new TextDecoder()
let buffer = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
const lines = buffer.split('\n')
buffer = lines.pop()
for (const line of lines) yield line
}
if (buffer) yield buffer
}
// Traiter CSV sans charger le fichier entier
const validRecords = await readLines(csvFile)
.drop(1) // Ignorer l'en-tête
.map(line => line.split(','))
.filter(cols => cols[2] === 'active')
.take(100)
.toArray()
Travailler avec des séquences infinies
Générer et traiter des flux de données infinis :
function* fibonacci() {
let [a, b] = [0, 1]
while (true) {
yield a
;[a, b] = [b, a + b]
}
}
// Trouver le premier nombre de Fibonacci supérieur à 1000
const firstLarge = fibonacci()
.find(n => n > 1000) // 1597
// Obtenir les 10 premiers nombres de Fibonacci pairs
const evenFibs = fibonacci()
.filter(n => n % 2 === 0)
.take(10)
.toArray()
Pagination d’API sans surcharge mémoire
Gérer efficacement les API paginées :
async function* fetchAllUsers(apiUrl) {
let page = 1
while (true) {
const response = await fetch(`${apiUrl}?page=${page}`)
const { data, hasMore } = await response.json()
for (const user of data) yield user
if (!hasMore) break
page++
}
}
// Traiter les utilisateurs sans charger toutes les pages
const premiumUsers = await fetchAllUsers('/api/users')
.filter(user => user.subscription === 'premium')
.map(user => ({ id: user.id, email: user.email }))
.take(50)
.toArray()
Considérations de performance et usage mémoire
Les aides d’itérateurs brillent quand :
- Traitement de données plus grandes que la mémoire disponible
- Vous n’avez besoin que d’un sous-ensemble de résultats
- Enchaînement de multiples transformations
- Travail avec des flux ou données temps réel
Elles sont moins adaptées quand :
- Vous avez besoin d’un accès aléatoire aux éléments
- Le jeu de données est petit et déjà en mémoire
- Vous devez itérer plusieurs fois (les itérateurs sont à usage unique)
Voici une comparaison mémoire :
// Approche tableau intensive en mémoire
function processLargeDataArray(data) {
return data
.map(transform) // Crée un nouveau tableau
.filter(condition) // Crée un autre tableau
.slice(0, 100) // Crée un troisième tableau
}
// Approche itérateur efficace en mémoire
function processLargeDataIterator(data) {
return data
.values()
.map(transform) // Pas de tableau intermédiaire
.filter(condition) // Pas de tableau intermédiaire
.take(100)
.toArray() // Seulement les 100 éléments finaux en mémoire
}
Support navigateur et polyfills
Les aides d’itérateurs JavaScript sont supportées dans :
- Chrome 122+
- Firefox 131+
- Safari 18.4+
- Node.js 22+
Pour les environnements plus anciens, utilisez le polyfill es-iterator-helpers :
npm install es-iterator-helpers
Pièges courants et solutions
Les itérateurs sont à usage unique
const iter = [1, 2, 3].values().map(x => x * 2)
console.log([...iter]) // [2, 4, 6]
console.log([...iter]) // [] - Déjà consommé !
// Solution : Créer un nouvel itérateur
const makeIter = () => [1, 2, 3].values().map(x => x * 2)
Mélanger les méthodes d’itérateur et de tableau
// Ne fonctionnera pas - filter retourne un itérateur, pas un tableau
const result = [1, 2, 3]
.values()
.filter(x => x > 1)
.includes(2) // Erreur !
// Solution : Convertir d'abord en tableau
const result = [1, 2, 3]
.values()
.filter(x => x > 1)
.toArray()
.includes(2) // true
Conclusion
Les aides d’itérateurs JavaScript apportent la programmation fonctionnelle à l’évaluation paresseuse, rendant possible le traitement efficace de jeux de données larges ou infinis. En comprenant quand utiliser .values()
ou Iterator.from()
et comment l’évaluation paresseuse diffère des méthodes de tableau empressées, vous pouvez écrire du code efficace en mémoire qui passe à l’échelle. Commencez à utiliser ces méthodes pour les données en flux, la pagination, et tout scénario où charger tout en mémoire n’est pas pratique.
FAQ
Les aides d'itérateurs standards fonctionnent seulement avec des itérateurs synchrones. Pour les opérations asynchrones, vous devrez attendre les aides d'itérateurs asynchrones (proposées pour les futures versions ES) ou utiliser des bibliothèques qui fournissent le support d'itération asynchrone.
Les aides d'itérateurs fournissent une évaluation paresseuse de base intégrée au langage, tandis que RxJS offre des fonctionnalités avancées comme la gestion d'erreurs, la contre-pression, et des opérateurs complexes. Utilisez les aides d'itérateurs pour des transformations simples et RxJS pour la programmation réactive complexe.
Non, les méthodes de tableau restent le meilleur choix pour les petits jeux de données qui tiennent en mémoire et quand vous avez besoin d'un accès aléatoire ou de multiples itérations. Les aides d'itérateurs complètent les tableaux pour des cas d'usage spécifiques impliquant des données larges ou infinies.
Oui, étendez la classe Iterator ou utilisez Iterator.from() avec un objet personnalisé implémentant le protocole d'itérateur. Cela vous permet d'ajouter des transformations spécifiques au domaine tout en maintenant la compatibilité avec les aides intégrées.