range() - Génère une plage de nombres
range() est une fonction de création de type instruction for qui émet un nombre spécifié d'entiers consécutifs à partir d'une valeur de départ spécifiée.
Vue d'ensemble
range() émet une séquence d'entiers consécutifs en tant qu'Observable en spécifiant une valeur de départ et le nombre d'entiers. Elle est utilisée pour la génération de nombres séquentiels et le traitement par lots comme moyen déclaratif pour remplacer l'instruction traditionnelle for.
Signature :
function range(
start: number,
count?: number,
scheduler?: SchedulerLike
): Observable<number>Paramètres :
start: La valeur de départ (à partir de laquelle l'émission doit commencer)count: le nombre de valeurs à publier (omis, de 0 à moins questart)scheduler: le planificateur pour émettre les valeurs (omis : émission synchrone)
Documentation officielle : 📘 RxJS Official : range()
Utilisation de base
Pattern 1 : Spécifier la valeur de départ et le nombre
C'est l'utilisation la plus courante.
import { range } from 'rxjs';
// Générer 5 nombres séquentiels à partir de 1 (1, 2, 3, 4, 5)
range(1, 5).subscribe({
next: value => console.log('Valeur:', value),
complete: () => console.log('Terminé')
});
// Sortie:
// Valeur: 1
// Valeur: 2
// Valeur: 3
// Valeur: 4
// Valeur: 5
// TerminéPattern 2 : Nombres séquentiels commençant à 0
En définissant la valeur de départ à 0, un nombre séquentiel comme un index de tableau peut être généré.
import { range } from 'rxjs';
// Nombres séquentiels de 0 à 9 (0, 1, 2, ..., 9)
range(0, 10).subscribe(console.log);
// Sortie: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9Pattern 3 : Commencer avec un nombre négatif
Les nombres négatifs peuvent également être générés.
import { range } from 'rxjs';
// 5 nombres séquentiels à partir de -3 (-3, -2, -1, 0, 1)
range(-3, 5).subscribe(console.log);
// Sortie: -3, -2, -1, 0, 1Caractéristiques importantes
1. Émission synchrone
Par défaut, range() émet toutes les valeurs de manière synchrone lors de l'abonnement.
import { range } from 'rxjs';
console.log('Avant abonnement');
range(1, 3).subscribe(value => console.log('Valeur:', value));
console.log('Après abonnement');
// Sortie:
// Avant abonnement
// Valeur: 1
// Valeur: 2
// Valeur: 3
// Après abonnement2. Se termine immédiatement
Notifie complete immédiatement après avoir émis toutes les valeurs.
import { range } from 'rxjs';
range(1, 3).subscribe({
next: val => console.log(val),
complete: () => console.log('Terminé!')
});
// Sortie: 1, 2, 3, Terminé!3. Équivalence avec l'instruction for
range(start, count) est équivalent à l'instruction for suivante.
// Instruction for impérative
for (let i = start; i < start + count; i++) {
console.log(i);
}
// range() déclaratif
range(start, count).subscribe(console.log);Cas d'utilisation pratiques
1. Traitement par lots
Utilisé pour exécuter plusieurs tâches de manière séquentielle.
import { range, of, Observable, concatMap, delay, map } from 'rxjs';
// Fonction pour simuler le traitement de données
function processItem(index: number): Observable<string> {
return of(index).pipe(
delay(100), // Simuler un temps de traitement de 100ms
map(i => `Résultat du traitement de l'élément ${i}`)
);
}
// Traiter séquentiellement 10 éléments de données (1 seconde de délai entre chaque traitement)
range(1, 10).pipe(
concatMap(index =>
processItem(index).pipe(delay(1000))
)
).subscribe({
next: result => console.log(`Traitement terminé: ${result}`),
complete: () => console.log('Tous les traitements terminés')
});
// Sortie:
// Traitement terminé: Résultat du traitement de l'élément 1 (après environ 1.1 secondes)
// Traitement terminé: Résultat du traitement de l'élément 2 (après environ 2.1 secondes)
// ...
// Traitement terminé: Résultat du traitement de l'élément 10 (après environ 10.1 sec.)
// Tous les traitements terminés2. Pagination
Récupérer plusieurs pages de données séquentiellement.
import { range, of, Observable, concatMap, delay } from 'rxjs';
interface PageData {
page: number;
items: string[];
}
// Fonction pour simuler la récupération de données de page
function fetchPage(page: number): Observable<PageData> {
return of({
page,
items: [`Element${page}-1`, `Element${page}-2`, `Element${page}-3`]
}).pipe(
delay(500) // Simuler un appel API
);
}
function fetchAllPages(totalPages: number) {
return range(1, totalPages).pipe(
concatMap(page => fetchPage(page))
);
}
fetchAllPages(5).subscribe({
next: (data: PageData) => console.log(`Page ${data.page}:`, data.items),
complete: () => console.log('Toutes les pages récupérées')
});
// Sortie:
// Page 1: ['Element1-1', 'Element1-2', 'Element1-3']
// Page 2: ['Element2-1', 'Element2-2', 'Element2-3']
// Page 3: ['Element3-1', 'Element3-2', 'Element3-3']
// Page 4: ['Element4-1', 'Element4-2', 'Element4-3']
// Page 5: ['Element5-1', 'Element5-2', 'Element5-3']
// Toutes les pages récupérées3. Traitement des index de tableau
Utiliser comme boucle basée sur l'index lors du traitement de chaque élément d'un tableau.
import { range, map } from 'rxjs';
const items = ['Pomme', 'Banane', 'Cerise', 'Datte', 'Sureau'];
range(0, items.length).pipe(
map(index => ({ index, item: items[index] }))
).subscribe(({ index, item }) => {
console.log(`[${index}] ${item}`);
});
// Sortie:
// [0] Pomme
// [1] Banane
// [2] Cerise
// [3] Datte
// [4] Sureau4. Génération de données de test
Utile pour générer des données fictives pour les tests unitaires.
import { range, map, toArray } from 'rxjs';
// Générer des données utilisateur fictives
range(1, 100).pipe(
map(id => ({
id,
name: `Utilisateur${id}`,
email: `utilisateur${id}@exemple.com`
})),
toArray()
).subscribe(users => {
console.log(`${users.length} utilisateurs générés`);
// Utiliser dans les tests
});5. Compteur pour le traitement de réessai
Contrôle le nombre de réessais en cas d'erreur.
import { range, throwError, concat, of, Observable, mergeMap, delay, catchError, map, toArray } from 'rxjs';
// Fonction pour simuler la récupération de données (échoue aléatoirement)
function fetchData(): Observable<string> {
const shouldFail = Math.random() > 0.7; // 30% de chances de succès
return of(shouldFail).pipe(
delay(300),
mergeMap(fail =>
fail
? throwError(() => new Error('Échec de récupération des données'))
: of('Récupération des données réussie')
)
);
}
function fetchWithRetry(maxRetries: number = 3) {
return concat(
range(0, maxRetries).pipe(
map(attempt => {
console.log(`Tentative ${attempt + 1}/${maxRetries}`);
return fetchData().pipe(
catchError(error => {
if (attempt === maxRetries - 1) {
return throwError(() => new Error('Nombre maximum de réessais atteint'));
}
return throwError(() => error);
}),
delay(Math.pow(2, attempt) * 1000) // Backoff exponentiel
);
}),
toArray()
)
);
}
fetchWithRetry().subscribe({
next: result => console.log('Résultat:', result),
error: err => console.error('Erreur:', err.message)
});
// Exemple de sortie:
// Tentative 1/3
// Tentative 2/3
// Résultat: Récupération des données réussieAsynchronisation avec le planificateur
Lors du traitement de grandes quantités de données, une exécution asynchrone est possible en spécifiant un planificateur.
import { range, asyncScheduler, observeOn } from 'rxjs';
console.log('Démarrage');
// Émettre 1 000 000 de nombres de manière asynchrone
range(1, 1000000).pipe(
observeOn(asyncScheduler)
).subscribe({
next: val => {
if (val % 100000 === 0) {
console.log(`Progression: ${val}`);
}
},
complete: () => console.log('Terminé')
});
console.log('Après abonnement (asynchrone, donc exécuté immédiatement)');
// Sortie:
// Démarrage
// Après abonnement (asynchrone, donc exécuté immédiatement)
// Progression: 100000
// Progression: 200000
// ...
// TerminéTIP
Utilisation du planificateur :
- Ne pas bloquer l'interface utilisateur lors du traitement de grandes quantités de données
- Contrôle du temps dans les tests (TestScheduler)
- Contrôle de la boucle d'événements dans l'environnement Node.js
Pour plus d'informations, veuillez consulter Types de planificateurs et comment les utiliser.
Comparaison avec d'autres fonctions de création
range() vs of()
import { range, of } from 'rxjs';
// range() - entiers consécutifs
range(1, 3).subscribe(console.log);
// Sortie: 1, 2, 3
// of() - énumérer des valeurs arbitraires
of(1, 2, 3).subscribe(console.log);
// Sortie: 1, 2, 3
// Différence: range() n'accepte que des nombres séquentiels, of() accepte des valeurs arbitraires
of(1, 10, 100).subscribe(console.log);
// Sortie: 1, 10, 100 (pas possible avec range())range() vs from()
import { range, from } from 'rxjs';
// range() - générer des nombres séquentiels
range(1, 5).subscribe(console.log);
// Sortie: 1, 2, 3, 4, 5
// from() - générer à partir d'un tableau (doit créer le tableau à l'avance)
from([1, 2, 3, 4, 5]).subscribe(console.log);
// Sortie: 1, 2, 3, 4, 5
// Avantage de range(): pas de pré-allocation de tableaux en mémoire
range(1, 1000000); // Efficace en mémoire
from(Array.from({ length: 1000000 }, (_, i) => i + 1)); // Le tableau va en mémoirerange() vs generate()
import { range, generate } from 'rxjs';
// range() - numérotation séquentielle simple
range(1, 5).subscribe(console.log);
// Sortie: 1, 2, 3, 4, 5
// generate() - exemple complexe de la même chose
generate(
1, // Valeur initiale
x => x <= 5, // Condition de continuation
x => x + 1 // Itération
).subscribe(console.log);
// Sortie: 1, 2, 3, 4, 5
// Avantages de generate(): condition et gestion d'état complexes
generate(
1,
x => x <= 100,
x => x * 2 // Incrémente par un facteur de 2
).subscribe(console.log);
// Sortie: 1, 2, 4, 8, 16, 32, 64
// (pas possible avec range())TIP
Critères de sélection :
- Nécessite des nombres séquentiels →
range() - Énumérer n'importe quelle valeur →
of() - Tableau/Promise existant →
from() - Condition/étape complexe →
generate()
Considérations sur les performances
Parce que range() émet des valeurs de manière synchrone, les performances doivent être prises en compte lors de la génération d'un grand nombre de valeurs.
WARNING
Gestion de grandes quantités de données :
// ❌ Mauvais exemple: émettre 1 million de valeurs de manière synchrone (l'interface utilisateur se bloque)
range(1, 1000000).subscribe(console.log);
// ✅ Bon exemple 1: asynchrone avec planificateur
range(1, 1000000).pipe(
observeOn(asyncScheduler)
).subscribe(console.log);
// ✅ Bon exemple 2: fractionnement par mise en mémoire tampon
range(1, 1000000).pipe(
bufferCount(1000)
).subscribe(batch => console.log(`${batch.length} éléments traités`));Choisir entre from() et tableau
import { range, from } from 'rxjs';
// Si vous avez besoin de nombres séquentiels → range() est plus concis
range(0, 10).subscribe(console.log);
// Pas besoin de créer un tableau puis de le convertir (inefficace)
from(Array.from({ length: 10 }, (_, i) => i)).subscribe(console.log);
// S'il existe un tableau existant → utilisez from()
const existingArray = [5, 10, 15, 20];
from(existingArray).subscribe(console.log);Gestion des erreurs
Bien que range() lui-même n'émette pas d'erreurs, des erreurs peuvent survenir dans le pipeline.
import { range, of, map, catchError } from 'rxjs';
range(1, 10).pipe(
map(n => {
if (n === 5) {
throw new Error('Erreur à 5');
}
return n * 2;
}),
catchError(error => {
console.error('Erreur survenue:', error.message);
return of(-1); // Retourner la valeur par défaut
})
).subscribe(console.log);
// Sortie: 2, 4, 6, 8, -1Résumé
range() est une fonction de création simple mais puissante qui produit une séquence d'entiers consécutifs.
IMPORTANT
Caractéristiques de range() :
- ✅ Idéale pour générer des nombres consécutifs (alternative à l'instruction for)
- ✅ Utile pour le traitement par lots, la pagination, la génération de données de test
- ✅ Efficace en mémoire (pas de pré-création de tableaux)
- ⚠️ Envisager l'asynchrone pour les grandes quantités de données
- ⚠️ Utiliser
generate()pour les conditions complexes
Sujets associés
- generate() - Génération de boucle générique
- of() - Énumère des valeurs arbitraires
- from() - Convertit depuis un tableau ou une Promise
- interval() - Émet des valeurs périodiquement