Skip to content

windowTime - diviser l'Observable à intervalles réguliers

L'opérateur windowTime regroupe les valeurs de l'Observable source à intervalles réguliers et produit le groupe en tant que nouvel Observable. Alors que bufferTime renvoie un tableau, windowTime renvoie Observable<T>, permettant à d'autres opérateurs d'être appliqués à chaque fenêtre.

🔰 Syntaxe de base et utilisation

ts
import { interval } from 'rxjs';
import { windowTime, mergeAll, take } from 'rxjs';

// 100msPublier la valeur chaque
const source$ = interval(100);

source$.pipe(
  windowTime(1000), // 1Fenêtre créée toutes les secondes
  take(3),          // Première3Une seule fenêtre
  mergeAll()        // Aplatir chaque fenêtre
).subscribe(value => {
  console.log('Valeur:', value);
});

// Sortie:
// 1Secondes: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
// 2Secondes: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
// 3Secondes: 20, 21, 22, 23, 24, 25, 26, 27, 28, 29
  • Une nouvelle fenêtre (Observable) est créée tous les temps spécifiés (1000 ms).
  • Chaque fenêtre peut être traitée comme une Observable indépendante.

🌐 Official RxJS documentation - windowTime

💡 Modèle d'utilisation typique.

  • Traitement par lots basé sur le temps : les données sont traitées par lots à intervalles de temps réguliers.
  • Agrégation de données en temps réel** : comptage du nombre d'événements par seconde
  • Contrôle des performances** : collecte de paramètres à intervalles réguliers.
  • Analyse des données de séries temporelles : traitement statistique par période de temps.

🔍 Différences avec bufferTime

TABLEAU_11___.

ts
import { interval } from 'rxjs';
import { bufferTime, windowTime, take } from 'rxjs';

const source$ = interval(100);

// bufferTime - Sortie sous forme de tableau
source$.pipe(
  bufferTime(1000),
  take(2)
).subscribe(values => {
  console.log('Tampon (tableau):', values);
  // Sortie: Tampon (tableau): [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
});

// windowTime - Observable Sortie en tant que
source$.pipe(
  windowTime(1000),
  take(2)
).subscribe(window$ => {
  console.log('Fenêtre (Observable):', window$);
  // Emboîté subscribe: window Modèles requis par la spécification de l'opérateur du système
  // (Voir aussiwindowTime A l'intérieur émis par Observable requis pour consommer)
  window$.subscribe(value => {
    console.log('  Valeur:', value);
  });
});

🧠 Exemple de code pratique 1 : Comptage des clics par seconde

Cet exemple montre comment compter le nombre de clics sur un bouton toutes les secondes.

ts
import { fromEvent } from 'rxjs';
import { windowTime, map, mergeAll, scan } from 'rxjs';

// Création de boutons
const button = document.createElement('button');
button.textContent = 'Cliquer';
document.body.appendChild(button);

// Zone de sortie
const output = document.createElement('div');
output.style.marginTop = '10px';
document.body.appendChild(output);

// Événement de clic
const clicks$ = fromEvent(button, 'click');

let windowNumber = 0;

clicks$.pipe(
  windowTime(1000), // 1Fenêtre créée toutes les secondes
  map(window$ => {
    ++windowNumber;

    // Compter le nombre de clics dans chaque fenêtre
    return window$.pipe(
      scan(count => count + 1, 0)
    );
  }),
  mergeAll()
).subscribe(count => {
  const timestamp = new Date().toLocaleTimeString();
  output.textContent = `[${timestamp}] Fenêtre ${windowNumber}: ${count}Cliqué une fois`;
});
  • Une nouvelle fenêtre est créée chaque seconde.
  • Le nombre de clics dans chaque fenêtre est compté en temps réel.

🎯 Exemple de code pratique 2 : Statistiques par période de temps

Cet exemple calcule la somme et la moyenne des valeurs pour chaque période de temps.

ts
import { interval } from 'rxjs';
import { windowTime, map, mergeMap, toArray, take } from 'rxjs';

// Création de la zone de sortie
const output = document.createElement('div');
output.innerHTML = '<h3>Traitement statistique pour chaque période de temps (toutes les1(chaque seconde)</h3>';
document.body.appendChild(output);

const table = document.createElement('table');
table.style.borderCollapse = 'collapse';
table.style.marginTop = '10px';
table.innerHTML = `
  <thead>
    <tr style="background: #f0f0f0;">
      <th style="border: 1px solid #ccc; padding: 8px;">Fenêtre</th>
      <th style="border: 1px solid #ccc; padding: 8px;">Nombre de cas</th>
      <th style="border: 1px solid #ccc; padding: 8px;">Total</th>
      <th style="border: 1px solid #ccc; padding: 8px;">Moyenne</th>
    </tr>
  </thead>
  <tbody id="stats-body"></tbody>
`;
output.appendChild(table);

const source$ = interval(100).pipe(
  map(() => Math.floor(Math.random() * 100)) // Valeur aléatoire
);

let windowNumber = 0;

source$.pipe(
  windowTime(1000), // 1(chaque seconde)
  take(5),          // 5Une seule fenêtre
  mergeMap(window$ => {
    const current = ++windowNumber;

    // Les valeurs de chaque fenêtre sont converties en tableau pour le traitement statistique.
    return window$.pipe(
      toArray(),
      map(values => ({
        window: current,
        count: values.length,
        sum: values.reduce((a, b) => a + b, 0),
        avg: values.length > 0
          ? (values.reduce((a, b) => a + b, 0) / values.length).toFixed(2)
          : 0
      }))
    );
  })
).subscribe(stats => {
  const tbody = document.getElementById('stats-body')!;
  const row = document.createElement('tr');
  row.innerHTML = `
    <td style="border: 1px solid #ccc; padding: 8px; text-align: center;">${stats.window}</td>
    <td style="border: 1px solid #ccc; padding: 8px; text-align: center;">${stats.count}</td>
    <td style="border: 1px solid #ccc; padding: 8px; text-align: center;">${stats.sum}</td>
    <td style="border: 1px solid #ccc; padding: 8px; text-align: center;">${stats.avg}</td>
  `;
  tbody.appendChild(row);
});
  • Les statistiques de chaque fenêtre peuvent être calculées individuellement.
  • Différents processus peuvent être appliqués à chaque fenêtre.
  • Présentation visuelle des données statistiques sous forme de tableau.

📊 Fenêtres qui se chevauchent (windowCreationInterval)

Vous pouvez faire se chevaucher des fenêtres en spécifiant windowCreationInterval comme second argument.

ts
import { interval } from 'rxjs';
import { windowTime, mergeMap, toArray, take, map } from 'rxjs';

// Création de la zone de sortie
const output = document.createElement('div');
output.innerHTML = '<h3>Fenêtres qui se chevauchent</h3>';
output.style.border = '1px solid #ccc';
output.style.padding = '10px';
output.style.marginTop = '10px';
document.body.appendChild(output);

const source$ = interval(100);

source$.pipe(
  windowTime(
    2000,  // Longueur de la fenêtre: 2secondes
    1000   // Intervalle entre les créations de fenêtres: 1secondes
  ),
  take(3),
  mergeMap((window$, index) =>
    window$.pipe(
      toArray(),
      map(values => ({ window: index + 1, values }))
    )
  )
).subscribe(result => {
  const div = document.createElement('div');
  div.style.marginTop = '10px';
  div.style.padding = '5px';
  div.style.backgroundColor = '#f5f5f5';
  div.style.borderLeft = '3px solid #4CAF50';

  const title = document.createElement('strong');
  title.textContent = `Fenêtre ${result.window}:`;
  div.appendChild(title);

  div.appendChild(document.createElement('br'));

  const values = document.createElement('span');
  values.textContent = `Valeur: [${result.values.join(', ')}]`;
  div.appendChild(values);

  div.appendChild(document.createElement('br'));

  const info = document.createElement('span');
  info.style.color = '#666';
  info.textContent = `(${result.values.length}Valeurs individuelles,${(result.window - 1)}secondes à${(result.window + 1)}secondes)`;
  div.appendChild(info);

  output.appendChild(div);

  // ChromeMesure: Rendu forcé
  void output.offsetHeight;
});

Description du comportement:

  • Fenêtre 1** : valeurs de 0 à 2 secondes [0, 1, 2, ... , 19] (20 valeurs)

  • Fenêtre 2** : valeurs de 1 à 3 secondes [10, 11, 12, ... , 29] (20) ← Les valeurs 10 à 19 se chevauchent avec la fenêtre 1.

  • Fenêtre 3** : valeurs de 2 à 4 secondes [20, 21, 22, ... , 39] (20 valeurs) ← Les valeurs 20-29 se chevauchent avec la fenêtre 2.

  • La création d'une nouvelle fenêtre avec un intervalle (1 seconde) plus court que la longueur de la fenêtre (2 secondes) entraînera une duplication.

  • Ceci est utile pour mettre en œuvre des fenêtres coulissantes.

🎯 Exemple pratique : surveillance d'événements en temps réel

ts
import { fromEvent } from 'rxjs';
import { windowTime, mergeMap, toArray, map } from 'rxjs';

// Zone de sortie
const output = document.createElement('div');
output.innerHTML = '<h3>Surveillance du mouvement de la souris (5(chaque seconde)</h3>';
document.body.appendChild(output);

const list = document.createElement('ul');
output.appendChild(list);

// Événement de déplacement de la souris
const moves$ = fromEvent<MouseEvent>(document, 'mousemove');

moves$.pipe(
  windowTime(5000), // 5(chaque seconde)
  mergeMap(window$ =>
    window$.pipe(
      toArray(),
      map(events => ({
        count: events.length,
        timestamp: new Date().toLocaleTimeString()
      }))
    )
  )
).subscribe(result => {
  const item = document.createElement('li');
  item.textContent = `[${result.timestamp}] Mouvement de la souris: ${result.count}temps`;
  list.insertBefore(item, list.firstChild);

  // Maximum10Affichage d'un maximum de
  while (list.children.length > 10) {
    list.removeChild(list.lastChild!);
  }
});

⚠️ Notes.

1. gestion des abonnements aux fenêtres

Chaque fenêtre est un Observable indépendant et doit faire l'objet d'un abonnement explicite.

ts
source$.pipe(
  windowTime(1000)
).subscribe(window$ => {
  // Emboîté subscribe: window Modèles requis par la spécification de l'opérateur du système
  // Aucune valeur ne circule à moins que la fenêtre elle-même ne soit abonnée à l'événement de mouvement de la souris.
  window$.subscribe(value => {
    console.log('Valeur:', value);
  });
});

ou aplatie en utilisant mergeAll(), concatAll(), switchAll(), etc.

ts
source$.pipe(
  windowTime(1000),
  mergeAll() // Fusionner toutes les fenêtres
).subscribe(value => {
  console.log('Valeur:', value);
});

2. gestion de la mémoire

Il est important de se désabonner correctement lors d'un fonctionnement prolongé.

ts
import { takeUntil } from 'rxjs';
import { Subject } from 'rxjs';

const destroy$ = new Subject<void>();

source$.pipe(
  windowTime(1000),
  mergeAll(),
  takeUntil(destroy$) // Désabonnement en cas de destruction
).subscribe();

// par exemple, lorsqu'un composant est détruit
destroy$.next();
destroy$.complete();

Spécifier la valeur maximale (maxWindowSize)

Le troisième argument permet de limiter le nombre maximum de valeurs pour chaque fenêtre.

ts
import { interval } from 'rxjs';
import { windowTime, mergeAll, take } from 'rxjs';

// 100msPublier la valeur chaque
const source$ = interval(100);

source$.pipe(
  windowTime(1000), // 1Fenêtre créée toutes les secondes
  take(3),          // Première3Une seule fenêtre
  mergeAll()        // Aplatir chaque fenêtre
).subscribe(value => {
  console.log('Valeur:', value);
});

// Sortie:
// 1Secondes: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
// 2Secondes: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
// 3Secondes: 20, 21, 22, 23, 24, 25, 26, 27, 28, 29

🆚 Comparaison des opérateurs basés sur les fenêtres

📚 Opérateurs apparentés.

  • bufferTime - regroupe les valeurs sous forme de tableau (version tableau de windowTime).
  • window - divise la fenêtre en émettant un Observable.
  • windowCount - partitionnement de la fenêtre sur la base d'un nombre de pièces.
  • windowToggle - contrôle de la fenêtre avec Observable de début et de fin.
  • windowWhen - découpage de fenêtres avec conditions de fermeture dynamiques

Résumé.

L'opérateur windowTime est un outil puissant pour diviser les flux sur une base temporelle et traiter chaque période comme une Observable indépendante.

  • ✅ Crée automatiquement des fenêtres à intervalles réguliers
  • ✅ Différents traitements peuvent être appliqués à chaque fenêtre
  • ✅ Les fenêtres coulissantes (duplicatas) sont prises en charge
  • Idéal pour l'agrégation et l'analyse de données en temps réel
  • ⚠️ Gestion de l'abonnement requise
  • ⚠️ Gestion de la mémoire

Publié sous licence CC-BY-4.0.