Ось готовий урок, створений спеціально за твоїм майстер-промптом. Вмикаємо режим CS50! 🚀
🎓 Урок: Observable та магія RxJS
Лектор: Твій цифровий двійник Девіда Малана 🎙️ Рівень: Початковий / Середній Стек: JavaScript / TypeScript, RxJS
1. 🔥 Вступ: Чому Promise — це ще не все?
Уявіть собі ситуацію. Ви замовили піцу. 🍕 Ви дзвоните, робите замовлення, і вам кажуть: "Чекайте, ми передзвонимо, коли кур'єр буде біля дверей". Це — класичний Promise. Ви зробили запит — отримали один результат (успіх або помилку). І все, історія закінчилася.
Але життя складніше. Що, як ви хочете отримувати не одну піцу, а підписатися на розсилку новин? Або, скажімо, ви розробляєте чат. Повідомлення приходять постійно: одне, друге, десяте...
Promise тут безсилий. Він "одноразовий". Ви не можете зробити promise.then() на кожне нове повідомлення в чаті, бо Promise резолвиться лише раз.
Риторичне запитання: Як нам обробити потік подій, який може ніколи не закінчуватися? Як реагувати на кожен клік мишки користувача, не створюючи "пекло з колбеків" (callback hell)?
Ось тут на сцену виходить RxJS і його головний герой — Observable (Спостережуваний об'єкт).
Аналогія: * Promise — це посилка з Amazon. Ви чекаєте, вона приходить одна. * Observable — це стрічка новин у Telegram. Пости можуть приходити щохвилини, раз на день або не приходити взагалі. Ви "підписуєтесь" на канал і реагуєте на кожен новий пост.
Сьогодні ми навчимося керувати часом і подіями. Поїхали!
2. 🧠 Теоретична база: Що там "під капотом"?
Давайте розберемо це без складних академічних термінів.
Observable (Потік) — це труба, по якій тече вода (дані). Але є нюанс. Ця труба магічна. Вода в ній не потече, поки ви не відкриєте кран.
Ключові поняття (запам'ятати обов'язково):
- Observable (Спостережуваний): Це той, хто створює дані (продюсер). Він може видати число, рядок, об'єкт, помилку або сигнал "я закінчив".
- Observer (Спостерігач): Це об'єкт, який знає, що робити з даними (консюмер). У нього є три методи:
next(value)— що робити, коли прийшли нові дані.error(err)— що робити, якщо сталася біда.complete()— що робити, коли потік закінчився (якщо закінчився).
- Subscription (Підписка): Це момент, коли ми з'єднуємо Observable і Observer. Це як натиснути кнопку "Підписатися" на YouTube.
- Operators (Оператори): Це інструменти, якими ми змінюємо дані прямо в трубі (фільтруємо, множимо, затримуємо).
💡 Як це працює (Інтуїтивно):
У звичайному програмуванні (наприклад, масиви) ви тягнете дані (Pull). Ви кажете: "Дай мені елемент під індексом 0". В RxJS дані самі штовхають вас (Push). Observable каже: "Гей! Ось нові дані! Тримай!".
Головний секрет: Observable — лінивий (lazy). Якщо ви створите Observable, але не викличете
.subscribe(), код всередині нього навіть не запуститься. Це як написати сценарій п'єси, але не найняти акторів. П'єси не буде.
3. 🧪 Приклади: Від "Hello World" до реальності
Для прикладів уявімо, що ми підключили бібліотеку RxJS.
Приклад 1: Найпростіший потік
Створимо потік, який просто видає три числа і замовкає.
import { Observable } from 'rxjs';
// 1. Створюємо Observable
const stream$ = new Observable(subscriber => {
console.log('Потік почався!');
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
subscriber.complete(); // Кажемо, що більше даних не буде
});
// 2. Підписуємось (відкриваємо кран)
console.log('До підписки');
stream$.subscribe({
next: value => console.log('Отримано:', value),
complete: () => console.log('Кінець фільму 🎬')
});
console.log('Після підписки');
Питання до вас: Що виведеться в консоль? У якому порядку? Подумайте секунду...
Результат:
До підписки
Потік почався!
Отримано: 1
Отримано: 2
Отримано: 3
Кінець фільму 🎬
Після підписки
Чому так? Тому що цей код синхронний. Як тільки ми викликали subscribe, функція всередині Observable виконалася миттєво.
Приклад 2: Асинхронність (Реальніший сценарій)
Тепер давайте імітуємо таймер.
import { Observable } from 'rxjs';
const timer$ = new Observable(subscriber => {
let count = 0;
const intervalId = setInterval(() => {
count++;
subscriber.next(count);
console.log(`Відправлено: ${count}`);
}, 1000);
// Важливий момент: функція очистки!
return function unsubscribe() {
clearInterval(intervalId);
console.log('Інтервал очищено!');
};
});
const subscription = timer$.subscribe(value => console.log('Я бачу:', value));
// Через 3.5 секунди ми відписуємось
setTimeout(() => {
subscription.unsubscribe();
}, 3500);
Що тут відбувається?
Ми запустили потік, який щосекунди "пушить" нам число. Але через 3.5 секунди ми сказали: "Досить!". Метод unsubscribe() викликав ту функцію, яку ми повернули з Observable, і зупинив setInterval. Без цього пам'ять би витікала, як вода з дірявого відра! 🪣
Приклад 3: Оператори (Магія pipe)
Чиста вода — це нудно. Давайте зробимо з неї лимонад.
Використаємо оператори of (для створення), map (трансформація) і filter (фільтрація).
import { of } from 'rxjs';
import { map, filter } from 'rxjs/operators';
// of(1, 2, 3, 4, 5) — це скорочення для new Observable...
const numbers$ = of(1, 2, 3, 4, 5);
numbers$.pipe(
filter(n => n % 2 !== 0), // Пропускаємо тільки непарні (1, 3, 5)
map(n => n * 10) // Множимо їх на 10
).subscribe(val => console.log(val));
Результат: 10, 30, 50.
Ми побудували конвеєр. Дані проходять крізь нього і змінюються.
4. 🛠 Практична частина
Час забруднити руки кодом! Відкривайте редактор (або уявно пишіть код).
Завдання 1: Копіпаст з розумінням
Повторіть "Приклад 1", але змініть його так, щоб він видавав ваші ім'я та прізвище, а потім помилку subscriber.error('Щось пішло не так') замість complete(). Подивіться, що станеться.
Завдання 2: Клік-клік
Використайте функцію fromEvent (з RxJS), щоб слухати кліки по документу.
import { fromEvent } from 'rxjs';
// Ваш код тут...
Зробіть так, щоб у консоль виводилися координати кліка (clientX, clientY).
Завдання 3: Виправ помилку Студент написав код, але в консолі — тиша. Чому?
import { interval } from 'rxjs';
import { map } from 'rxjs/operators';
const seconds$ = interval(1000).pipe(map(x => x * 2));
// ... і пішов пити каву ☕
Підказка: Згадайте про "лінивість".
Завдання 4: Міні-кейс "Світлофор" 🚦 Створіть Observable, який кожну секунду видає кольори: спочатку "Red", потім "Yellow", потім "Green". Після "Green" він має завершитись. Підпишіться і виведіть кольори в консоль.
Завдання 5: Питання "А що, якщо..."
Що буде, якщо в Прикладі 2 ми не викличемо unsubscribe()? Як довго працюватиме setInterval? (Спробуйте уявити це в контексті Single Page Application, коли користувач перейшов на іншу сторінку).
5. 💡 Мислення як у розробника
Як відрізнити новачка від профі в RxJS?
- Новачок думає імперативно: "Візьми дані, потім зроби це, потім перевір це". Він часто пише
subscribeвсередині іншогоsubscribe(це називається Nested Subscription і це гріх!). - Профі думає потоками: "Є потік кліків. Я хочу трансформувати цей потік у потік запитів на сервер".
Типові помилки:
* Забули відписатися. Це вбивця продуктивності. Завжди думайте: "Коли цей потік має померти?". В Angular, наприклад, це часто роблять через AsyncPipe, щоб не відписуватись вручну.
* Логіка всередині subscribe.
* ❌ Погано: Отримати дані в subscribe, там їх іфувати, множити і обробляти.
* ✅ Добре: Використати pipe() і оператори, щоб у subscribe прийшов вже готовий результат.
Порада: Додавайте знак долара
$в кінці назви змінної, якщо це Observable (наприклад,userClicks$). Це не обов'язково синтаксично, але це правило гарного тону. Бачиш долар — знаєш, що це потік, на який треба підписатися.
6. 🧩 Підсумок
Ну що, друзі, видихаємо! 😤 Сьогодні ми зробили величезний крок. Ми перейшли від простого очікування однієї події до керування цілими потоками часу.
Що ви тепер вмієте:
1. Розумієте різницю між Promise (один раз) і Observable (багато разів).
2. Знаєте, що Observable — ліниві (нема підписки — нема діла).
3. Вмієте створювати прості потоки даних і трансформувати їх через pipe.
4. Розумієте важливість unsubscribe.
Що далі?
Ви запитаєте: "Добре, я вмію фільтрувати числа. Але як мені зробити пошук, який не спамить сервер при кожній букві (debounce)? Або як об'єднати дані з трьох різних API в один об'єкт?"
На наступному уроці ми розберемо Магію Операторів: mergeMap, switchMap та combineLatest. Це буде справжній рок-н-рол! 🎸
А поки що — практикуйтесь і не забувайте підписуватись (на потоки)! 😉