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

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

Створення потоків

Щоб працювати з потоками, їх треба звідкись брати. Функцій створення в RxJS десятки, але в реальному коді ти використовуватимеш шість-сім. Розберемо саме їх — і одразу з прикладами, де вони справді потрібні.

of — готові значення

import { of } from 'rxjs';

of(1, 2, 3).subscribe(console.log);        // 1, 2, 3, потім complete
of([1, 2, 3]).subscribe(console.log);      // [1,2,3] одним значенням — типова плутанина

// головне застосування: підставити готові дані замість запиту
getTasks(): Observable<Task[]> {
  return of(MOCK_TASKS);
}

of — основа всього мок-шару з уроку 11. Він віддає значення синхронно й одразу завершує потік.

from — із того, що вже є

import { from } from 'rxjs';

from([1, 2, 3]);                    // три окремі значення (на відміну від of([...]))
from(fetch('/api/tasks'));          // з промайса
from('abc');                        // 'a', 'b', 'c' — з будь-чого ітерованого

timer і interval — за часом

import { timer, interval } from 'rxjs';

timer(1000);              // одне значення через секунду, потім complete
timer(0, 5000);           // одразу, далі кожні 5 секунд — не завершується сам
interval(1000);           // кожну секунду, перше значення через секунду
Ці потоки не завершуються самі

interval і timer з другим аргументом працюють, доки їх не зупинити. Якщо на такий потік підписатись у компоненті й не відписатись, він продовжить крутитись після того, як компонент зникне з екрана. Це і є класичний витік памʼяті — про нього урок 10.

throwError — контрольована помилка

import { throwError } from 'rxjs';

// саме так: фабрика, а не готовий обʼєкт помилки
throwError(() => new Error('Сервер недоступний'));

У мок-сервісі це спосіб показати, як застосунок поводиться при збої — без справжнього бекенду. Обовʼязкова частина критеріїв: «graceful error handling for mocked async operations».

EMPTY і NEVER

import { EMPTY, NEVER } from 'rxjs';

EMPTY;    // одразу complete, жодного значення — зручно повертати з catchError
NEVER;    // нічого не робить ніколи — переважно в тестах

fromEvent — події DOM

import { fromEvent } from 'rxjs';

fromEvent<KeyboardEvent>(document, 'keydown').pipe(
  filter(e => e.key === 'Escape'),
).subscribe(() => this.close());

Це саме той випадок, коли RxJS дає більше, ніж addEventListener: події можна фільтрувати, обмежувати частотою й комбінувати з іншими потоками — і все це в одному місці, з автоматичним прибиранням слухача при відписці.

defer — відкласти створення

import { defer, of } from 'rxjs';

// ❌ дата обчислиться один раз, у момент створення потоку
const wrong$ = of(new Date());

// ✅ дата обчислюватиметься на кожну підписку
const right$ = defer(() => of(new Date()));

defer потрібен щоразу, коли потік має брати актуальний стан у момент підписки, а не той, що був при створенні. Наприклад, коли URL запиту залежить від поточного значення фільтра.

HttpClient теж повертає Observable

this.http.get<Task[]>('/api/tasks');
this.http.get<Task[]>('/api/tasks', { params: { status: 'todo' } });
this.http.post<Task>('/api/tasks', payload);
this.http.patch<Task>(`/api/tasks/${id}`, patch);
this.http.delete<void>(`/api/tasks/${id}`);

Це холодний потік, який завершується після відповіді. Дженерик у кутових дужках — те саме правило з модуля 2: тип відповіді вказується явно, ніяких any.

ТребаБереш
Віддати готові даніof(value)
Розгорнути масив у потік значеньfrom(array)
Обгорнути промісfrom(promise)
Зробити щось із затримкоюtimer(ms)
Періодичне опитуванняtimer(0, ms)
Змоделювати збійthrowError(() => err)
Нічого не повернути, але завершитиEMPTY
Слухати подію DOMfromEvent(el, 'click')
Порахувати щось у момент підпискиdefer(() => …)