Занятие 8.
RxJS и функциональное программирование
SIS.Angular.2018
Кернер Денис
План занятия
Что такое RxJS
Для чего оно нужно
О функциональном программировании
Примеры
О RxJS
Примеры
Итоги
Reactive extensions for JS
Библиотека для работы с событиями и потоками данных
Интенсивно используется в Angular
- Event emitter
- HTTP
- Async pipe
- Router
- Reactive Forms
Давайте изучать?
Есть маленький нюанс..
Кривая изучения RxJS без попытки понять основы функционального программирования.
Функциональное программирование (ФП)
ФП (лямбда исчисление) - программирование на основе функций.
// левая часть - определение функции
// правая часть - выражение которое ее вычисляет
// = означает что левую часть можно заменять правой,
// в выражениях использующих эту функцию
f(x) = y
f(x) = 2 * x
f(x) = x * x
// и т.д.
Основные свойства ФП:
- Чистые функции
- Функции как объекты первого рода
- Функции высших порядков
- Иммутабельность
- Замыкания
- Ссылочная прозрачность (левую часть можно заменить правой)
Применяя эти свойства, мы получаем:
- Декларативность
- Тестируемость
- Более красивый код
- Возможность комбинировать функции между собой, решая огромное количество задач, малыми средствами
- Рекурсия!
Недостатки ФП:
- Выше уровень абстракции
- Выше порог вхождения
- Сложность с обучающим материалом – много материала, пытающегося сразу нагрузить математикой, без практики
Что же делать? Больше практики!
- Отсортировать список
- Убрать записи по условию
- Сконвертировать записи (из объекта в число)
- Вычислить минимальное/среднее значение
Еще больше практики!
- Делаем плоский массив из вложенного
function concatAll(array) {
function concatAllAcc(element, accumulator) {
if (!Array.isArray(element)) {
accumulator.push(element);
} else {
if (element.length > 0) {
concatAllAcc(element[0], accumulator);
concatAllAcc(element.slice(1), accumulator);
}
}
}
let res = [];
concatAllAcc(array, res);
return res;
}
Итоги:
Несмотря на всю академичность, ФП вполне успешно применяется в продакшне:
- Твиттер написан на Scala (backend)
- Росработа написана на Scala (backend)
- Ericsson применял Erlang в своих телекоммуникационных проектах
Подходы из ФП активно проникают и в другие языки,
например лямбды и потоки в java 8
Что почитать:
Проблема: Обработка событий на колбэках сложна и чревата ошибками. Callback hell
Частичное решение: Промисы
Полное решение: RxJS
Reactive extensions for JavaScript
Голливудский принцип:
Есть два вида работы с коллекциями - когда мы сами запрашиваем значения (pull), и когда мы подписываемся на получение новых значений (push).
Push коллекции
- pull - итераторы
- push - observables
- Observable
-
Observer next *(error|complete)?
- Subscription
- Operators
- Subject multicast
Основные элементы:
Observable = Iterable * Observer
Пример создания Observable
var observable = new Observable(function (observer) {
observer.next(1);
observer.next(2);
setTimeout(() => {
observer.next(3);
observer.complete();
}, 1000);
});
console.log('just before subscribe');
observable.subscribe({
next: x => console.log('got value ' + x),
error: err => console.error('error occurred: ' + err),
complete: () => console.log('done'),
});
console.log('just after subscribe');
just before subscribe
got value 1
got value 2
just after subscribe
got value 3
done
Важность subscribe
Пока не будет подписки через метод subscribe - мы не получим никаких значений.
Это еще называется cold observables
В чем мощь наблюдаемых объектов?
В операторах и их сочетаниях с помощью pipe
Типы операторов:
- Создания - from, fromPromise
- Преобразования - map
- Фильтрации - filter
- Комбинирования - combineLatest, merge
- Обработки ошибок - retry, catchError
- Агрегации - reduce, scan
Marble diagrams для изучения RxJS
Marble diagrams = диаграммы c шариками
Временная ось. слева направо
Значения что посылает Observable
Успешное
завершение
Пунктир показывает трансформацию значений исходного Observable. Прямоугольник показывает
оператор
Новый Observable - результат оператора
Ошибка трансформации, не умеем переворачивать круги
Примеры операторов: filter, map
Диаграмма комбинирования Observables
Последовательное применение операторов.
Как применить последовательно? Pipe.
import { map, switchMap, throttle } from 'rxjs/operators';
myObservable
.pipe(map(data => data * 2), switchMap(...), throttle(...))
.subscribe(...);
Практика
Пример: автокомплит
const { fromEvent } = Rx;
const { map, throttleTime, filter } = RxOperators;
fromEvent(document, 'mousemove').pipe(
map(event => event.clientX),
filter(x => x > 300),
throttleTime(300)
)
const searchBox = document.getElementById('search-box');
const typeahead = fromEvent(searchBox, 'input').pipe(
map((e: KeyboardEvent) => e.target.value),
filter(text => text.length > 2),
debounceTime(10),
distinctUntilChanged(),
switchMap(() => ajax('/api/endpoint'))
);
typeahead.subscribe(data => {
// Handle the data from the API
});
Пример: реагируем на клики только в правой части экрана
Что посмотреть/почитать
Домашнее задание
Тренируемся с rxJs:
Применяем операторы:
- pipe
- debounceTime
- debounce
- timer
- interval
- fromEvent
Спасибо за внимание!
Вопросы?
Занятие 8
By Dennis Kerner
Занятие 8
rxjs
- 303