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

В чем разница между Tap и операторами изменения Observable?

2.0 Middle🔥 141 комментариев
#Архитектура и паттерны

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

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

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

Разница между tap() и операторами изменения Observable

Основная разница

tap() — это оператор, который позволяет выполнить побочные эффекты (side effects) без изменения потока данных. Все остальные операторы (map, filter, switchMap и т.д.) трансформируют сами данные в потоке. Это ключевое различие между ними.

tap() — наблюдение без изменения

tap() (ранее называался do) используется для:

  • Логирования значений
  • Отладки
  • Выполнения побочных эффектов (API запросы, сохранение в локальное хранилище)
  • Отправки данных в другие системы

Оперь важный момент: tap() не изменяет данные, он просто пропускает их дальше по цепочке:

import { of } from 'rxjs';
import { tap, map } from 'rxjs/operators';

of(1, 2, 3)
  .pipe(
    tap(value => console.log('Значение в tap:', value)),
    map(value => value * 2)
  )
  .subscribe(result => console.log('Итоговый результат:', result));

// Консоль:
// Значение в tap: 1
// Итоговый результат: 2
// Значение в tap: 2
// Итоговый результат: 4
// Значение в tap: 3
// Итоговый результат: 6

map() — трансформация данных

map() трансформирует каждое значение в потоке и передает новое значение дальше:

of(1, 2, 3)
  .pipe(
    map(value => value * 2)  // Трансформирует: 1->2, 2->4, 3->6
  )
  .subscribe(result => console.log(result));

// Консоль:
// 2
// 4
// 6

Практический пример с учетом побочных эффектов

// Получение данных пользователя
getUser(userId)
  .pipe(
    tap(user => console.log('Пользователь получен:', user)), // Логирование
    map(user => user.email),                                   // Извлечение email
    tap(email => localStorage.setItem('userEmail', email)),   // Сохранение в storage
    switchMap(email => validateEmail(email))                   // API запрос валидации
  )
  .subscribe(
    isValid => console.log('Email валиден:', isValid)
  );

Операторы изменения данных

Вот основные операторы трансформации данных в Observable:

map() — преобразование каждого значения:

of({ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' })
  .pipe(
    map(user => user.name)  // Извлекаем имена
  )
  .subscribe(console.log);

// Alice
// Bob

filter() — отфильтровать значения по условию:

of(1, 2, 3, 4, 5)
  .pipe(
    filter(value => value > 2)  // Только значения > 2
  )
  .subscribe(console.log);

// 3
// 4
// 5

switchMap() — переключается на новый Observable (отменяет предыдущий):

of(1, 2, 3)
  .pipe(
    switchMap(id => getUser(id))  // Для каждого id получаем данные пользователя
  )
  .subscribe(user => console.log(user));

mergeMap() — объединяет несколько Observable (не отменяет предыдущие):

of(1, 2, 3)
  .pipe(
    mergeMap(id => getUser(id))  // Все запросы выполняются параллельно
  )
  .subscribe(user => console.log(user));

flatMap() — синоним mergeMap():

of(1, 2, 3)
  .pipe(
    flatMap(id => getUser(id))  // Равносильно mergeMap
  )
  .subscribe(user => console.log(user));

Когда использовать tap()

  1. Логирование и отладка:
api.getUsers()
  .pipe(
    tap(users => console.log('Получено пользователей:', users.length)),
    map(users => users.filter(u => u.active))
  )
  .subscribe(...);
  1. Побочные эффекты без изменения данных:
formSubmit
  .pipe(
    tap(formData => analytics.trackEvent('Form submitted')),
    switchMap(formData => submitForm(formData))
  )
  .subscribe(...);
  1. Сохранение в хранилище:
authService.login(credentials)
  .pipe(
    tap(token => localStorage.setItem('token', token)),
    tap(token => this.token = token)
  )
  .subscribe(...);

Обработка ошибок

tap() может обрабатывать ошибки через второй параметр:

api.getData()
  .pipe(
    tap(
      value => console.log('Данные:', value),
      error => console.error('Ошибка:', error),
      () => console.log('Завершено')
    )
  )
  .subscribe(...);

Ключевые отличия

Характеристикаtap()map()filter()switchMap()
Изменяет данныеНетДаДаДа (на новый Observable)
Возвращаемое значениеИгнорируетсяИспользуетсяtrue/falseObservable
Побочные эффектыОсновное назначениеПобочныеНетВозможны
ИспользованиеЛогирование, отладкаТрансформация данныхФильтрацияАсинхронные операции

Производительность

  • tap() не требует создания новых объектов, только выполняет функцию
  • map() создает новый объект/значение для каждого элемента
  • Перед использованием map() с побочными эффектами всегда спрашивай себя: нужно ли изменить данные? Если нет — используй tap()
В чем разница между Tap и операторами изменения Observable? | PrepBro