Принципы функционального и реактивного программирования не новы для JavaScript, но в последнее время их применение стало широко распространенным в большинстве современных фреймворков и наборов инструментов. Простота использования этих подходов улучшилась, поскольку мы, наконец, увидели упадок устаревших браузеров и введение функциональных и реактивных парадигм в ES6 и ES8.
Во-первых, давайте быстро рассмотрим появляющиеся шаблоны в текущем состоянии JavaScript.
Функциональное программирование
Функциональное программирование — это набор методов для создания кода, не создающего побочных эффектов. В его основе лежит концепция чистых функций, где их возвращаемое значение определяется только аргументами, и он не изменяет эти аргументы или что-либо вне этой функции в процессе. За прошедшие годы в JavaScript было добавлено множество функциональных шаблонов, в первую очередь API для работы с массивами и итераторами.
Данные в рамках функционального программирования должны быть неизменяемыми. На простейшем уровне, если объект может изменить свое значение после создания, он является изменяемым. JavaScript, как правило, очень изменчивый язык, и требуется немало внимания и усилий, чтобы обеспечить неизменяемость нужных частей исходного кода, и эти подходы формируют основу для значительной части современных фреймворков JavaScript.
Функциональное программирование также предполагает, что вещи не имеют состояния. В общем, состояние относится к информации, которая доступна и может использоваться вашим исходным кодом в определенный момент времени. Код с отслеживанием состояния заботится о текущем состоянии, тогда как код без сохранения состояния всегда выполняет операции так, как если бы они выполнялись в первый раз. Чистая функция по своей природе не имеет состояния. Приложения без состояния по-прежнему управляют состоянием, что приводит к значительной путанице, но они могут возвращать свое состояние, не изменяя свое предыдущее состояние. Так что подумайте об этом, поскольку каждое состояние во времени является новым представлением текущего состояния, а не попыткой переписать старое состояние в новое состояние.
Чтобы работать со всем этим, часто существует некоторая форма кода контейнера или контроллера, который будет управлять побочными эффектами и работать в разных состояниях, сохраняя остальную часть вашего кода чистой и свободной от побочных эффектов.
Реактивное программирование
Реактивное программирование — это управляемая событиями парадигма, которая публикует и прослушивает события с течением времени декларативным образом. Сообществу JavaScript потребовалось более десяти лет, чтобы действительно заставить реактивное программирование работать достаточно хорошо. Рассмотрим некоторые из вещей, которые были опробованы на этом пути:
- вызовы функций для геттеров/сеттеров (подход Dojo 1), чтобы обернуть любое интересующее свойство в дополнительный вызов функции, чтобы его можно было отслеживать
- watch , ранняя, но неэффективная попытка Firefox отслеживать изменения свойств.
- MutationEvents, ранний стандарт W3C DOM для отслеживания всех изменений DOM, который был слишком медленным для надежного использования.
- Нативные геттеры/сеттеры ES6, важный строительный блок
- MutationObservers, более быстрый способ отслеживания изменений DOM, но все еще ограниченный DOM.
- Object.observe и его упадок, в ранних версиях Dojo 2 была прокладка с улучшениями
- RxJS Observables, что очень хорошо, но RxJS делает намного больше, чем Observables, которые вам могут понадобиться, а могут и не понадобиться.
- Наблюдаемые объекты ES8
RxJS популяризировал реактивное программирование в JavaScript, и одна из его основных функций, Observables, теперь является частью ES8, а Dojo 2 предоставляет прокладку для Observable API.
Наблюдаемые объекты работают как массивы, которые возникают асинхронно во времени. Реализация Observables в ES8 может рассматриваться как более простая версия потоков. Как с Observables, так и с потоками исходный код может отслеживать изменения с течением времени, и почти все можно превратить в Observable или Stream. Это может помочь думать о Stream или Observable как о множестве промисов, испускаемых с течением времени.
Функциональное реактивное программирование
Теперь, когда вы сочетаете мощь функционального и реактивного программирования с современным фреймворком и набором API, вы выходите за пределы мира предыдущих инструментов и фреймворков и получаете преимущества, которые вы получаете с React+ Redux или Dojo 2. Проще говоря, функционально реактивный программирование дает нам декларативный и свободный от побочных эффектов исходный код, который эффективно работает при изменении значений с течением времени. Это означает, что функциональное реактивное программирование по умолчанию является динамическим с уменьшенной сложностью, и оно может перемещаться между состояниями и эффективно управлять изменениями состояний.
Додзё 2
Хотя многие из этих концепций существовали в различных формах в Dojo 1.x, Dojo 1 не был чисто функциональной реактивной средой программирования. Он проделал большую работу по продвижению этих концепций, но это были существенные улучшения JavaScript, браузеров и общих мыслей о том, как эффективно достичь этого с помощью JavaScript и/или TypeScript.
В Dojo 2 эти концепции лежат в основе того, как мы пишем исходный код и приложения. Например, посмотрите на ранний пример использования части пакета @dojo/stores:
const baseStore = createQueryStore({ data: [ { id: ‘item-1’, foo: 2, bar: ‘a’ }, { id: ‘item-2’, foo: 1, bar: ‘b’ } ] }); const viewOne = createQueryStore<{id: string; foobar: string }>(); const viewTwo = createQueryStore<{ value: number, id: string }>(); viewOne.observe().subscribe((storeDelta) => { console.log(‘View one’); console.log(storeDelta.afterAll); }); viewTwo.filter((item) => item.value <= 10).sort(‘value’).observe().subscribe((storeDelta) => { console.log(‘View two’); console.log(storeDelta.afterAll); }); materialize({ source: baseStore.transform((item) => ({ id: item.id, foobar: `${item.foo}-${item.bar}` }), ‘id’), target: viewOne }); materialize({ source: baseStore.transform((item) => ({ id: item.id, value: item.foo }), ‘id’), target: viewTwo }); baseStore.add([ { id: ‘new-item’, foo: 11, bar: ‘c’ }, { id: ‘new-item2’, foo: 12, bar: ‘d’ } ]) .then(() => baseStore.delete(‘new-item’)) .then(() => baseStore.put({ id: ‘new-item2’, foo: -1, bar: ‘zero’ }));
В этом примере вы увидите, как мы можем наблюдать за изменениями в магазине с помощью viewOne.observe()
, а затем подписываться на эти изменения. Затем мы можем взять набор данных, отфильтровать результаты, отсортировать их, затем наблюдать за любыми изменениями и, наконец, подписаться на любые обновления с помощью viewTwo.filter((item) => item.value <= 10).sort('value').observe().subscribe(
.
API materialize позволяет нам определять преобразование каждого элемента в нашем магазине, отображая данные из исходного формата в желаемый формат в наших представлениях.
Все это вместе объединяет некоторые стандартные паттерны функционального реактивного программирования. Этот подход используется в нескольких частях Dojo 2, в частности во всем, что отслеживает изменения в данных или свойствах, таких как виджеты или хранилища данных.
В приведенном выше примере выводится следующее:
View one [] View two [] View one [ { id: 'item-1', foobar: '2-a' }, { id: 'item-2', foobar: '1-b' } ] View two [ { id: 'item-2', value: 1 }, { id: 'item-1', value: 2 } ] View one [ { id: 'item-1', foobar: '2-a' }, { id: 'item-2', foobar: '1-b' }, { id: 'new-item', foobar: '11-c' }, { id: 'new-item2', foobar: '12-d' } ] View one [ { id: 'item-1', foobar: '2-a' }, { id: 'item-2', foobar: '1-b' }, { id: 'new-item2', foobar: '12-d' } ] View one [ { id: 'item-1', foobar: '2-a' }, { id: 'item-2', foobar: '1-b' }, { id: 'new-item2', foobar: '-1-zero' } ] View two [ { id: 'new-item2', value: -1 }, { id: 'item-2', value: 1 }, { id: 'item-1', value: 2 } ]
Достижение вышеизложенного без принципов функционального реактивного программирования потребует значительно больше усилий, вызовет дополнительные проблемы с синхронизацией и приведет к увеличению объема и уязвимости исходного кода.
Первоначально опубликовано на SitePen.com