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

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

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

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

  2. «FRP завершает обмен сообщениями с потоком управления». Можете ли вы (кто-нибудь) дать более конкретное объяснение?

  3. Разве я не могу обойти наблюдаемые конечные точки FRP так же, как и каналы?

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


person tillda    schedule 17.12.2013    source источник
comment
Не рекомендуется передавать наблюдаемые наблюдаемые вокруг.   -  person Aleš Roubíček    schedule 18.12.2013
comment
Это не совсем так, так как это вполне возможно, и рекомендуется для таких вещей, как публикация/подписка, особенно с combLatest, когда он сведен.   -  person Matthew Podwysocki    schedule 19.12.2013


Ответы (5)


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

Абстракции

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

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

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

О завершении потока управления

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

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

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

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

Реализация

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

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

Последние мысли

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

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

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

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

person guilespi    schedule 26.12.2013
comment
CSP можно использовать для обработки потоков, а наблюдаемые объекты — для высокоуровневой бизнес-логики. Я не вижу, как тот или другой лучше разделяет проблемы. - person Max Heiber; 20.09.2016

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

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

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

person oskarkv    schedule 22.12.2013
comment
Фреймворки Rx имеют простые API (а именно планировщики) для объявления, когда и в каком потоке должен выполняться код. Это не обязательно должен быть поток, который замечает/производит событие. - person erikprice; 10.11.2014
comment
@erikprice Хорошо! Я на самом деле не использовал Rx, просто быстро прочитал об этом. Я все еще думаю, что то, о чем я говорил, имел в виду Рич. - person oskarkv; 10.11.2014

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

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

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

person noisesmith    schedule 17.12.2013
comment
Спасибо за Ваш ответ. Я добавил еще некоторые детали, которые я не понимаю. - person tillda; 18.12.2013
comment
FRP принципиально не относится к обратным вызовам. обратные вызовы - это просто средство для целей гораздо более высокого порядка. Если бы речь шла только об обратных вызовах, мы бы назвали это старым добрым реактивным программированием. - person cwharris; 19.12.2013
comment
Согласен с @christopher-harris, что речь идет не только об обратных вызовах, так как вы можете получить хорошее композиционное поведение без него, например, differentUntilChanged, retry, sample, Throttle и т. д. Есть много операторов, которые вообще не требуют обратного вызова. Это больше о цепочке, чем о чем-либо еще. - person Matthew Podwysocki; 19.12.2013
comment
Кроме того, я не знаю ни одной библиотеки FRP, которая запрещала бы вам отправлять наблюдаемое (канал FRP, по вашим словам) из другого наблюдаемого. - person Tim Pote; 06.10.2014
comment
Кроме того, в FRP канал не является объектом первого класса (т.е. вы не можете отправить канал FRP в качестве значения на канале FRP. Поскольку в инфраструктурах Rx нет каналов, я предполагаю, что вы имеете в виду наблюдаемые/сигналы. Что неверно — вы, безусловно, можете отправлять наблюдаемые/сигналы как значения других наблюдаемых/сигналов. - person erikprice; 03.12.2014
comment
где именно сказано, что core.async — это порт блоков Go из Go? просто любопытно. - person Erik Kaplun; 24.06.2019

РЕДАКТИРОВАТЬ:

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

  1. Да, во многих случаях обратные вызовы могут быть устранены, например, с помощью логики повторных попыток, DifferentUntilChanged и многих других вещей, таких как:

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

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

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

  3. Да, вы можете передавать объект так же, как и канал, как в случае с объектом ниже.

Например, здесь вы можете вести себя как канал, используя 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);

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

person Matthew Podwysocki    schedule 18.12.2013
comment
Я ценю мысль, которую вы вложили в это, но на самом деле это не ответ на вопрос. - person Chuck; 19.12.2013
comment
Извините, я отвечал на другой, но в то время у меня не было достаточно представителей, чтобы сделать это. Отредактировано, чтобы дать лучший ответ. - person Matthew Podwysocki; 19.12.2013

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

person Robert Monfera    schedule 06.03.2015