Angular IDPStrong Junior
Модулі / Дані, моки й RxJS / Сімейство *Map
🔥 0

Модуль 4 · урок 4 з 13

Сімейство *Map

Це найважливіший урок модуля й найчастіше питання на співбесідах. Усі чотири оператори роблять одне: беруть значення, створюють з нього новий потік і підмішують його результати у вихідний. Різниця тільки в одному — що робити, якщо нове значення прийшло, поки попередній внутрішній потік ще не завершився.

Задача, яку вони вирішують

// ❌ так не працює: map поверне Observable всередині Observable
query$.pipe(
  map(q => this.api.search(q)),   // Observable<Observable<Task[]>>
).subscribe(result => …);          // result — це потік, а не дані

// ✅ *Map розгортає вкладений потік
query$.pipe(
  switchMap(q => this.api.search(q)),   // Observable<Task[]>
).subscribe(tasks => …);

Чотири стратегії

ПРИЙШЛИ ТРИ ЗНАЧЕННЯ, ЗАПИТИ ТРИВАЛІ switchMap лишається тільки останній mergeMap усі паралельно, порядок не гарантовано concatMap строго по черзі, порядок збережено exhaustMap решта проігнорована, поки перший не завершився
Різниця лише в реакції на нове значення під час роботи попереднього: скасувати, запустити паралельно, стати в чергу або проігнорувати.

switchMap — скасувати попереднє

searchQuery$.pipe(
  switchMap(q => this.api.search(q)),
);

Коли: пошук, фільтри, завантаження деталей при зміні вибраного елемента — усе, де потрібен лише останній результат.

Чому саме він: користувач набирає «kyi», потім «kyiv». Запит по «kyi» більше не потрібен — гірше того, він може повернутись пізніше за запит по «kyiv» і перезаписати правильний результат неправильним. switchMap скасовує попередню підписку й знімає цю проблему повністю.

mergeMap — усе паралельно

selectedIds$.pipe(
  mergeMap(id => this.api.delete(id)),   // видалити всі відмічені одночасно
);

Коли: незалежні операції, де порядок не має значення й скасовувати нічого не треба.

Обережно: без обмеження одночасності можна вистрілити сотню запитів разом. Другим аргументом задається ліміт: mergeMap(fn, 3).

concatMap — строго по черзі

saveActions$.pipe(
  concatMap(action => this.api.save(action)),   // наступний старт лише після завершення попереднього
);

Коли: порядок критичний — послідовні збереження, черга дій, синхронізація офлайн-змін. Мінус очевидний: якщо один запит зависне, черга стане.

exhaustMap — ігнорувати, поки зайнятий

fromEvent(saveButton, 'click').pipe(
  exhaustMap(() => this.api.save(form.value)),   // подвійний клік не створить другий запит
);

Коли: захист від повторних натискань, логін, будь-яка дія, яку не можна запускати двічі. Це найелегантніше рішення проблеми «користувач клікнув кнопку тричі».

Як вибрати за десять секунд

ПитанняОператорТиповий випадок
Потрібен лише останній результат?switchMapПошук, фільтри, деталі вибраного
Усі важливі, порядок байдужий?mergeMapМасові незалежні дії
Усі важливі, порядок критичний?concatMapЧерга збережень
Поки зайнятий — ігноруємо нові?exhaustMapКнопка «Зберегти», логін
Найчастіша помилка

switchMap для збереження чи видалення. Якщо користувач швидко натисне двічі, перший запит скасується — і сервер може отримати лише другий. Для дій, які змінюють дані, бери concatMap або exhaustMap. Просте правило: switchMap — для читання, concat/exhaust — для запису.