Подтвердить что ты не робот

Сравнение core.async и функционального реактивного программирования (+ Rx)

Кажется, я немного смущен при сравнении Clojure core.async с так называемыми реактивными расширениями (Rx) и FRP в общем. Они, похоже, решают подобную проблему асинхронности, поэтому я задаюсь вопросом, каковы главные различия и в каких случаях один предпочтительнее другого. Может кто-нибудь объяснить?

РЕДАКТИРОВАТЬ: Чтобы стимулировать более подробные ответы, я хочу уточнить вопрос:

  • Core.async позволяет писать синхронный код. Однако, как я понимаю, FRP требует только один уровень вложенных обратных вызовов (вся функция, обрабатывающая логику, передается как аргументы API FRP). Похоже, что оба подхода делают ненужные пирамиды обратного вызова. Это правда, что в JS мне приходится писать function() {...} много раз, но основная проблема, вложенные обратные вызовы, также удалена в FRP. Правильно ли я понимаю?

  • " FRP собирает сообщения сообщений с потоком управления" Можете ли вы (кто-то) дать более конкретное объяснение?

  • Не могу ли я передавать наблюдаемые конечные точки FRP так же, как я пропускаю каналы?

В общем, я понимаю, откуда берутся оба подхода, и я попробовал несколько учебников в обоих из них. Однако я, кажется, "парализован" неочевидностью различий. Есть ли какой-нибудь пример кода, который трудно записать в одном из них и легко использовать другой? И какова архитектурная причина этого?

4b9b3361

Ответ 1

Я думаю, что основная проблема заключается в том, что ваше предположение о решаемой проблеме не совсем так, поскольку ни одна из них не занимается проблемой асинхронности.

Абстракции

FRP Основная идея - это распространение изменений, подумайте о том, чтобы сделать то же самое, что делает Excel, где вы определяете ячейки в зависимости друг от друга в каскаде, и когда одна ячейка изменяется, все зависимые ячейки на каскаде пересчитываются.

core.async Основная идея - это системная декомпозиция, думайте как разделяющие проблемы, используя queue в середине различных процессов, а в случае core.async вместо очередей у ​​вас есть каналы, но вы получаете идею.

Таким образом, удаление пирамидального кода не является целью любой технологии, и они работают на разных уровнях абстракции.

О выборе потока управления

Идея о компиляции и управлении потоком взята из исходного сообщения асинхронного ядра.

Несмотря на то, что существуют различные механизмы для очистки событий/обратных вызовов (FRP, Rx/Observables), они не меняют своей фундаментальной природы, а именно, что при событии выполняется произвольное количество другого кода, возможно, в том же потоке, что приводит к предупреждениям, таким как "не делайте слишком много работы в обработчике" и фразы типа "callback hell".

Перефразируя, если у вас есть код бизнес-домена внутри обработчика событий, вы собрали обработку событий X с помощью , что делать, когда X происходит.

Это то, что решает core.async, так как введение очереди/канала посередине помогает улучшить разделение проблем.

Реализация

Все ваши вопросы, касающиеся обратных вызовов и передачи наблюдаемых конечных точек в качестве параметров, являются только вопросами реализации, это действительно зависит от реализации Rx и API.

Если вы посмотрите на React повторно используемые компоненты, у вас действительно нет большого количества обратного ада, и вы получите эту идею прохождение наблюдаемых вокруг.

Заключительные мысли

Даже если Rx можно использовать для моделирования любого потока данных, более часто используется для UI Rendering a-la Excel, чтобы упростить изменение вашего представления при изменении вашей модели.

С другой стороны, core.async может использоваться для моделирования разделения проблем, когда любые две подсистемы взаимодействуют друг с другом (такой же сценарий использования, что и очереди), используя его в основной цепочке рендеринга интерфейса, заключается в разделении:

  • Генерация и обработка событий на стороне сервера
  • Как это событие влияет на вашу модель.

Таким образом, вы можете иметь core.async и FRP вместе, так как core.async будет разделять проблемы, а FRP будет определять ваш каскадный поток данных после обновления вашей модели.

Ответ 2

По крайней мере одно из основных принципиальных различий и, я думаю, то, что Rich с помощью "[FRP] составляет сообщение сообщений с потоком управления", заключается в следующем.

Обратные вызовы - это код, который выполняется. И это выполнение должно произойти в некотором потоке в определенный момент времени. Часто время, когда происходит событие, и поток - это поток, который замечает/создает событие. Если производитель вместо этого отправляет сообщение на канал, вы можете использовать это сообщение, когда захотите, в любом потоке, который вы хотите. Таким образом, обратные вызовы, которые по существу являются формой связи, составляют связь с потоком управления, определяя, когда и где выполняется код обратного вызова. Если вам нужно использовать обратные вызовы по какой-либо причине, просто используйте их, чтобы поместить сообщение в очередь/канал, и вы вернулись на дорожку.

Поскольку core.async менее сложный, он должен быть предпочтительным, если нет оснований для использования альтернатив.

Ответ 3

Clojure core.async - это портовый блок go на языке Go. Основной концепцией являются каналы, представляющие асинхронную связь между потоками.

Цель состоит в том, чтобы иметь возможность писать нормально выглядящие блоки последовательного кода, которые получают доступ к каналам для ввода данных, и это легко переводится в конечный автомат, который работает CSP для вас.

Контрастность с FRP, которая по-прежнему принципиально связана с обратными вызовами, и она объединяет сообщения сообщений с потоком управления. core.async полностью исключает обратные вызовы из вашего кода и отделяет поток управления от передачи сообщений. Кроме того, в FRP канал не является объектом первого класса (т.е. Вы не можете отправлять FRP-канал в качестве значения на канале FRP).

Ответ 4

EDIT:

Чтобы ответить на ваши вопросы:

  • Да, много раз обратные вызовы могут быть устранены, например, с помощью логики повтора, distinctUntilChanged и множества других вещей, таких как:

    var obs = getJSON('story.json').retry(3);

    var obs = Rx.Observable.fromEvent(document, 'keyup').distinctUntilChanged();

  • Я не уверен, что это означает, что это усложняет управление потоком.

  • Да, вы можете обойти объект, как если бы вы использовали канал, как и для объекта ниже.

Например, здесь вы можете использовать канал как поведение, используя RxJS с ReplaySubject:

var channel = new Rx.ReplaySubject();

// Send three observables down the chain to subscribe to
channel.onNext(Rx.Observable.timer(0, 250).map(function () { return 1; }));
channel.onNext(Rx.Observable.timer(0, 1000).map(function () { return 2; }));
channel.onNext(Rx.Observable.timer(0, 1500).map(function () { return 3; }));

// Now pass the channel around anywhere!
processChannel(channel);

Чтобы сделать это немного более конкретным, сравните код из сообщения Дэвида Нолена здесь с примером FRP RxJS здесь

Ответ 5

Здесь есть сообщение, в котором сравнивается FRP с CSP по ограниченному набору примеров (что, однако, предназначено для демонстрации преимуществ CSP), с заключением в конце: http://potetm.github.io/2014/01/07/frp.html