Принципы функционального и реактивного программирования не новы для 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