Модуль 4 · урок 1 з 13
Що таке Observable
RxJS — це те, на чому спотикаються всі, хто приходить в Angular. Насправді вся складність тримається на одній ідеї: дані — це не значення, яке ти взяв, а потік, на який ти підписався. Якщо прийняти цю думку по-справжньому, решта модуля стає технікою.
Значення проти потоку
// значення: ти береш його, і воно є
const tasks = getTasks();
console.log(tasks.length);
// потік: ти кажеш, що робити, коли значення прийде
getTasks$().subscribe(tasks => console.log(tasks.length));
Різниця не в синтаксисі. Значення існує зараз і одне. Потік описує, що станеться з часом — і значень у ньому може бути нуль, одне або нескінченно багато.
| Одне значення | Багато значень | |
|---|---|---|
| Синхронно | Звичайна змінна | Масив |
| Асинхронно | Promise | Observable |
Ось і все місце Observable у картині світу: це масив, розтягнутий у часі.
Три канали
source$.subscribe({
next: value => console.log('значення', value), // може статись багато разів
error: err => console.error('помилка', err), // один раз, потік закінчено
complete: () => console.log('усе'), // один раз, потік закінчено
});
complete,
або error. Після нього потік мертвий — це найважливіша деталь усього уроку.Помилка вбиває потік. Якщо в стрімі пошуку не обробити помилку, після першого ж невдалого запиту поле пошуку перестане працювати назавжди — і жодних повідомлень у консолі не буде. Це найпоширеніший баг новачків у RxJS, і урок 7 цілком про те, як його не зробити.
Лінивість: нічого не станеться без підписки
const request$ = this.http.get<Task[]>('/api/tasks');
// запиту ще НЕ БУЛО — це лише опис того, що станеться
request$.subscribe(tasks => …); // ось тепер запит пішов
request$.subscribe(tasks => …); // а це ДРУГИЙ запит, не той самий
Це головна відмінність від Promise. Проміс починає роботу в момент створення
й запамʼятовує результат. Observable — це рецепт: скільки разів підписався, стільки разів
він і виконається.
Холодні й гарячі потоки
| Холодний | Гарячий | |
|---|---|---|
| Коли починає | При кожній підписці | Живе сам, незалежно від підписників |
| Що бачать підписники | Кожен свою окрему копію | Спільні значення, з моменту підписки |
| Приклад | http.get(), of() | Події миші, WebSocket, Subject |
| Аналогія | Замовити фільм — почнеться спочатку | Прямий ефір — приєднався й дивишся з поточного моменту |
// зробити холодний потік спільним, щоб запит пішов один раз на всіх
readonly tasks$ = this.http.get<Task[]>('/api/tasks').pipe(
shareReplay({ bufferSize: 1, refCount: true })
);
shareReplay знадобиться, коли той самий потік читають кілька компонентів
або кілька async pipe у шаблоні. Без нього кожен | async — це
окремий HTTP-запит.
Observable проти Promise
| Promise | Observable | |
|---|---|---|
| Скільки значень | Рівно одне | Скільки завгодно |
| Коли стартує | Одразу при створенні | При підписці |
| Скасування | Немає | unsubscribe() |
| Повторний запуск | Ні, результат кешується | Так, кожна підписка заново |
| Оператори | then, catch | Понад сотня, комбінуються в pipe |
«Observable — це потік значень у часі. На відміну від промайса, він лінивий: нічого
не відбувається, поки не підпишешся, і кожна підписка запускає роботу заново. Його можна
скасувати через unsubscribe, і до нього можна застосувати оператори.
Помилка завершує потік назавжди, тому її треба ловити всередині pipe.»