← Назад к вопросам

В чем разница между Subject, BehaviorSubject и ReplaySubject?

1.0 Junior🔥 111 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

В чем разница между Subject, BehaviorSubject и ReplaySubject

Это вопрос про RxJS — библиотеку реактивного программирования. Subjects — это специальные Observable'ы, которые позволяют отправлять значения подписчикам. Они используются в Angular и других фреймворках для управления состоянием.

Subject

Subject — это простой Subject, который отправляет значения только подписчикам, которые подписались до отправки значения.

import { Subject } from 'rxjs';

const subject = new Subject<string>();

// Первый подписчик
subject.subscribe(value => {
  console.log('Subscriber 1:', value);
});

// Отправляем значение
subject.next('Hello'); // Выведет: Subscriber 1: Hello

// Второй подписчик ПОСЛЕ отправки
subject.subscribe(value => {
  console.log('Subscriber 2:', value); // НЕ выведет ничего!
});

// Отправляем ещё значение
subject.next('World'); 
// Subscriber 1: World
// Subscriber 2: World

Ключевая особенность: Subject не хранит последнее значение. Подписчики пропускают все значения, отправленные до их подписки.

Когда использовать Subject:

  • Простая передача событий
  • Пользовательские события
  • Когда не нужно сохранять историю значений

BehaviorSubject

BehaviorSubject — это Subject, который:

  1. Требует начальное значение при создании
  2. Всегда имеет текущее значение
  3. Сразу отправляет последнее значение новым подписчикам
import { BehaviorSubject } from 'rxjs';

// Требует начальное значение
const subject = new BehaviorSubject<string>('Initial');

// Первый подписчик
subject.subscribe(value => {
  console.log('Subscriber 1:', value);
});
// Выведет: Subscriber 1: Initial (сразу!)

// Отправляем значение
subject.next('Hello');
// Subscriber 1: Hello

// Второй подписчик ПОСЛЕ отправки
subject.subscribe(value => {
  console.log('Subscriber 2:', value);
});
// Выведет: Subscriber 2: Hello (сразу получит последнее значение!)

// Отправляем ещё значение
subject.next('World');
// Subscriber 1: World
// Subscriber 2: World

// Получить текущее значение
console.log(subject.getValue()); // 'World'

Ключевая особенность: BehaviorSubject всегда имеет значение и отправляет его новым подписчикам сразу.

Когда использовать BehaviorSubject:

  • Состояние приложения (текущий пользователь, тема, язык)
  • Когда нужно иметь текущее значение
  • Angular Services для управления состоянием

ReplaySubject

ReplaySubject — это Subject, который хранит несколько последних значений и отправляет их новым подписчикам.

import { ReplaySubject } from 'rxjs';

// Хранит последние 3 значения
const subject = new ReplaySubject<string>(3);

// Отправляем значения
subject.next('A');
subject.next('B');
subject.next('C');
subject.next('D');
subject.next('E');

// Первый подписчик
subject.subscribe(value => {
  console.log('Subscriber 1:', value);
});
// Выведет: C, D, E (последние 3 значения)

// Отправляем новое значение
subject.next('F');
// Subscriber 1: F

// Второй подписчик
subject.subscribe(value => {
  console.log('Subscriber 2:', value);
});
// Выведет: D, E, F (последние 3 значения)

Буферизация по времени:

// Хранит значения за последние 1000ms
const subject = new ReplaySubject<string>(100, 1000);

subject.next('Старое значение');

// Ждём 1.1 секунды
setTimeout(() => {
  subject.next('Новое значение');
  
  subject.subscribe(value => {
    console.log('Subscriber:', value);
  });
  // Выведет только: Subscriber: Новое значение
  // Старое значение вышло из временного окна
}, 1100);

Ключевая особенность: ReplaySubject буферизирует значения по количеству или времени.

Когда использовать ReplaySubject:

  • История действий (undo/redo)
  • Кэширование последних значений
  • Логирование событий
  • Когда нужна история, но не вся (как BehaviorSubject с памятью)

Сравнение в таблице

СвойствоSubjectBehaviorSubjectReplaySubject
Начальное значениеНе требуетсяТребуетсяНе требуется
Текущее значениеНетДа (getValue())Буфер последних
Отправляет новым подписчикамНетДа, последнееДа, из буфера
Хранит историюНет1 значениеN значений
Использование памятиМинимумМалоЗависит от буфера

Практический пример в Angular Service

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';

interface User {
  id: number;
  name: string;
}

@Injectable()
export class UserService {
  // BehaviorSubject для состояния пользователя
  private userSubject = new BehaviorSubject<User | null>(null);
  public user$ = this.userSubject.asObservable();
  
  // ReplaySubject для истории действий
  private actionsSubject = new ReplaySubject<string>(5);
  public actions$ = this.actionsSubject.asObservable();
  
  // Subject для уведомлений о событиях
  private notificationSubject = new Subject<string>();
  public notification$ = this.notificationSubject.asObservable();

  loadUser(id: number) {
    // Имитация загрузки
    const user = { id, name: 'John' };
    
    // Отправляем пользователя всем подписчикам
    this.userSubject.next(user);
    
    // Логируем действие
    this.actionsSubject.next(`User ${id} loaded`);
    
    // Отправляем уведомление
    this.notificationSubject.next('User loaded successfully');
  }
  
  getCurrentUser(): User | null {
    // Только BehaviorSubject позволяет получить текущее значение
    return this.userSubject.getValue();
  }
}

// В компоненте
@Component({
  selector: 'app-user',
  template: `
    <div *ngIf="user$ | async as user">
      {{ user.name }}
    </div>
  `
})
export class UserComponent implements OnInit {
  user$ = this.userService.user$;
  actions$ = this.userService.actions$;
  notification$ = this.userService.notification$;

  constructor(private userService: UserService) {}

  ngOnInit() {
    this.userService.loadUser(1);
    
    // История всех действий
    this.actions$.subscribe(action => {
      console.log('History:', action);
    });
  }
}

Выбор Subject для разных случаев

Используй Subject когда:

  • События без состояния (клики, форм-сабмиты)
  • Просто отправляешь события подписчикам

Используй BehaviorSubject когда:

  • Управляешь состоянием приложения
  • Нужно получить текущее значение (getValue)
  • Новые подписчики должны знать текущее состояние

Используй ReplaySubject когда:

  • Нужна история (undo/redo)
  • Новые подписчики должны видеть прошлые значения
  • Кэширование последних запросов

Это фундаментальные концепции для работы с реактивным программированием в JavaScript/TypeScript.

В чем разница между Subject, BehaviorSubject и ReplaySubject? | PrepBro