Pull to refresh

Comments 50

Реактивное программирование — это когда ты вместо обработки событий по одному объединяешь их в поток и затем работаешь уже только с ним.

Кул. Вот и еще один взял и сузил все варианты реактивного программирования до потоков событий. Бедные, бедные акторы.

Акторы и реактивное программирование — перпендикулярные концепции, они о разном. Реактивное программирование — это именно концептуализация потоков распространения изменений состояния, акторы — это концептуализация параллельных вычислений (хотя и возможно реализовать одно с помощью другого, семантически они не эквивалентны). Или вы имели ввиду что-то ещё?
Акторы и реактивное программирование — перпендикулярные концепции, они о разном

Расскажите это Рональду Куну, автору (одному из) книги Reactive Design Patterns, который считает акторов одним из способов реализации реактивного программирования: "Actors are asynchronous and non-blocking and do support message passing. They do scale up to use multiple cores on a machine. They also scale outward across nodes in both the Erlang and Akka implementations. They provide supervision mechanisms as well in support of fault tolerance. They meet all of the requirements for building Reactive applications."


Заодно Кун — один из авторов Reactive Manifesto и один из авторов курса Принципы реактивного программирования, в котором акторы тоже упоминаются как равноправный инструмент.


Окей, не нравится Кун? Вот вам Вернон, Reactive Messaging Patterns with the Actor Model.


Реактивное программирование — это именно концептуализация потоков распространения изменений состояния

А откуда вы взяли это определение, и почему вы считаете, что оно верное?

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

Я нигде и не говорю, что они семантически эквивалентны. Я всего лишь говорю, что модель акторов — это такая же равноправная реализация реактивного программирования, как и потоки событий (Rx).


Собственно, Reactive Manifesto — который, извините, немного первичнее, чем обсуждаемый пост — выделяет четыре основных характеристики реактивной системы. Акторы им соответствуют. Rx им тоже соответствует (хотя, по утверждению Куна, и в несколько меньшей мере).


Так на основании чего вы утверждаете, что реактивное программирование — это только и обязательно потоки изменения состояния?

«Я всего лишь говорю, что пельмени — это такая же равноправная реализация еды, как и борщ. Иванов считает, что борщ даже лучше еда, чем пельмени. Так на основании чего вы утверждаете, что еда — это только и обязательно пельмени?»

Реактивное программирование — это еда, а не борщ или пельмени, если вдруг аналогия оказалась непонятной.

Продолжая вашу аналогию, в статье утверждают, что единственная еда — жидкая, а вся твердая еда — от лукавого.

В статье с заголовком «Борщ — отличная еда» вы ожидали рассказа о пельменях? Не очень понятна ваша претензия. О еде в статье написано всё верно, это именно явно описываемые потоки распространения событий изменения состояния, даже если вы предпочитаете делать акцент не на преобразователях данных, а на объектах, в которые упаковали эти преобразователи.
В статье с заголовком «Борщ — отличная еда» вы ожидали рассказа о пельменях?

А вы не заметили, что я спорю не с заголовком, а с конкретным определением?


это именно явно описываемые потоки распространения событий изменения состояния, даже если вы предпочитаете делать акцент не на преобразователях данных, а на объектах, в которые упаковали эти преобразователи.

Я предпочитаю делать акцент на том, что это не потоки.


Таки я еще раз вас спрошу, на основании чего вы утверждаете, что реактивное программирование — это обязательно "потоки распространения событий изменения состояния"?

На основании всех источников, в том числе приведённых вами.

Для вас, похоже, поток — это что-то типа new Stream, а не концепция.
На основании всех источников, в том числе приведённых вами.

Каких "всех"? Приведенные мной источники эту концепцию не поддерживают.


А в самых первых строчках статьи — общая концепция.

А теперь откроем английский текст: "reactive programming is a programming paradigm oriented around data flows and the propagation of change". Внимание, вопрос: как из этого определения вытекает "вместо обработки событий по одному объединяешь их в поток и затем работаешь уже только с ним"?


Data flow — это вполне может быть цепочка разнородных событий "голова к хвосту", те же future, нанизанные через map.

В программировании много что переводится как "поток" — и flow, и stream, и даже thread, но при этом это совершенно разные вещи.


А доказать я ничего не хочу, просто уже не первый раз все понятие реактивного программирования сводят к Rx (и в этой статье тоже).

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

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


Flow — это поток, вне зависимости от того, что бывают и иные виды потоков.

Это только одно из значений. Совершенно не обязательно — правильное и подходящее в данном случае.

Вы сами с собой спорите? Теперь и flow вас не устраивает?

Меня "не устраивает" перевод слова flow как "поток", учитывая существующие в индустрии коннотации — и особенно в контексте поста про Rx.

Какой перевод вам бы понравился?

Зависит от фразы целиком. Если речь идет о приведенном выше определении из английской википедии, я бы говорил о "движении данных и распространении изменений".

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

Эмм, а где "разворачивание"? Английская фраза звучит как "[centered around] data flows and propagation of change". Я просто взял другой вариант перевода слова flow.

Акторы ближе к переводу flow, чем потоки? Какие в индустрии коннотации? Как, по-вашему, нужно было перевести?
Акторы ближе к переводу flow, чем потоки?

Акторы не имеют никакого отношения к переводу flow — как и reactive extensions.


Какие в индустрии коннотации?

"Поток однородных элементов", как в IO.Stream.


Как, по-вашему, нужно было перевести?

Я уже отвечал the_ghost, перевод "движение данных" кажется мне более уместным.

И чем ваш перевод лучше? Вы уверены, что тот лингвистический дефект, что вы пытаетесь исправить, не дефект вашего личного лексикона?
И чем ваш перевод лучше?

Тем, что он не вносит (как минимум) третье значение для уже существующего термина.


Вы уверены, что тот лингвистический дефект, что вы пытаетесь исправить, не дефект вашего личного лексикона?

Я уверен, что не только я использую термин "поток" в качестве перевода терминов "stream" и "thread".

Все остальные, пользующиеся терминами только как заклинаниями, не понимая их смысла. По-вашему, и слово «объект» уже использовано, и слово «прототип», а потому нужно говорить «некая сущность» и «прообраз сущности», чтобы не смутить программистов на JS смешиванием терминологии. Ведь русским языком программисты, как известно, не владеют, только языком программирования.
Ведь русским языком программисты, как известно, не владеют, только языком программирования.

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


По-вашему, и слово «объект» уже использовано, и слово «прототип», а потому нужно говорить «некая сущность» и «прообраз сущности», чтобы не смутить программистов на JS смешиванием терминологии.

Это не по-моему, это вы мне приписываете нечто, чего я не говорил.

Это аналогия, утрированная, чтобы выразить своё мнение о том, что вы слишком смело свои личные трудности перевода (неспособность принять абстракцию «поток» за пределами IO.Streams или pthreads) спроецировали на остальных. Позабыв уже об акторах.

Мне, пожалуй, надоело пытаться что-то до вас донести. Всё, что я хотел озвучить по отношению к этому посту, я уже озвучил, и автор меня услышал.

Фокус на RxJS объясним — доклад будет именно об этой библиотеке.
вопрос: как из этого определения вытекает «вместо обработки событий по одному объединяешь их в поток и затем работаешь уже только с ним»?

Моё определение вытекло вовсе не из приведенного вами. Считайте это «js фронт-енд» квинтэссенцией всех определений о реактивном программировании. Вы сами обратили внимание на количество вариантов. Мы даже xkcd вставили в статью, чтобы не воспринимали это определение как единственно верное.
Считайте это «js фронт-енд» квинтэссенцией всех определений о реактивном программировании.

Можно добавить это ограничение в текст статьи?

Попробуем завтра подправить. Сегодня редакторы спят. Так сказать, подписались на поток сновидений.
https://ru.m.wikipedia.org/wiki/%D0%A0%D0%B5%D0%B0%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5

Ваш случай — раздел «Объектно-ориентированное реактивное программирование». А в самых первых строчках статьи — общая концепция.
и третьего потока net, который будет собирать данные из net и gross

Net собирает данные из самого себя или это ошибка?
Опечатка по тексту — net собирает данные из gross и taxes. Код в статье и на jsfiddle верный.
«Например, в жизни с «реактивностью» вы сталкиваетесь в Excel»

Александр Соловьев, разлогиньтесь пожалуйста.
ЗЫ. Советую послушать весь данный доклад
Отличный доклад, да. Особенно полезен тем, кто желает «раздуплить» реактивность

В KnockoutJS, MeteorJS, $jin_atom нет ни стримов, ни событий, но есть реактивное программирование. Как же так? :-)


По примеру с экселем, как вы на стримах реализуете пользовательские формулы так, чтобы на изменение любой ячейки не происходил пересчёт всей таблицы?


Знаете, от доклада на конференции ожидаешь более системного подхода, упорядоченного, всестороннего рассмотрения вопроса, а не просто описания API одной кривоватой библиотеки, подмены понятий и однобокого мнения.

Как может идти дождь без ног, так и Meteor описывает потоки изменений состояния без объекта DataFlow. Реактивность — это даже не архитектура, это всего лишь абстрактный принцип, который выражаться в конкретной архитектуре может очень по-разному. Но суть этого принципа всегда заключается в удобстве работы с потоками данных.
Реактивное программирование — это когда ты вместо обработки событий по одному объединяешь их в поток и затем работаешь уже только с ним.

Тут явным образом описан event-stream, а не data-flow.

Я прочитал эту фразу с акцентом на повышении уровня абстракции. Т.е., автор, как мне показалось, говорит о наведении порядка в изменении состоянии приложения путём организации разрозненных обработчиков событий в декларативно обозначенные (хоть акторами выражено, хоть атомами, хоть трансдюсерами) _потоки_ распространения изменений состояния приложения. (Но я, наверное, наконец смог прочитать эту фразу и по-вашему.)
Knockout — в первую очередь это View. Meteor — цельный фреймфорк. В knockout повсюду торчат Observable. C Meteor / jin не работал, деталей не знаю. Сильно много «реактивно» на Knockout не попишешь — не хватает доступных методов для работы с observable — filter/combineLatest/flatMap. Да и по виду код Knockout очень слабо похож на bacon/rx/kefir — мало в нем реактивного.

По excel. Не совсем понятно условие «любое изменение не пересчитывает всю таблицу». Возможно имеется в виду ситуация, когда значение в зависимой ячейке изменилось на такое же? Для этого есть методы distinct / distinctUntilChanged, которые откидывают повторные значения (более тонкую разницу можно увидеть сравнив диаграмы или почитав http://reactivex.io/documentation/operators/distinct.html

А вот слова при «кривоватость» библиотеки настораживают… В чем кривость-то? На данный момент у RxJS самое обширное АПИ по работе с Observable.
Сильно много «реактивно» на Knockout не попишешь — не хватает доступных методов для работы с observable — filter/combineLatest/flatMap. Да и по виду код Knockout очень слабо похож на bacon/rx/kefir — мало в нем реактивного


эти методы — буква F в FRP, отсутствие этих методов делает Knockout менее функциональным, но никак не менее реактивным. К тому же без них вполне неплохо живётся, просто вместо a.add(b) пишется a+b и необходимость в изучении 100500 методов на все случаи жизни сразу отпадает.
Knockout — в первую очередь это View. Meteor — цельный фреймфорк.

И что? Модели реактивности там построены не вокруг потоков событий.


В knockout повсюду торчат Observable.

Которые являются реактивными переменными, а не стримами.


C Meteor / jin не работал, деталей не знаю.

А вы почитайте. Первый основан на идее "перезапусков", второй — как и нокают на идее "реактивных переменных".


Сильно много «реактивно» на Knockout не попишешь — не хватает доступных методов для работы с observable — filter/combineLatest/flatMap.

Они и не нужны. В том же нокауте:


var filteredList = ko.computed( () => sourceList.filter( i => i % 2 ) ) // filter
var ballance = ko.computed( () => debet() + credit() ) // combileLatest

Что делает flatMap затрудняюсь сказать.


Да и по виду код Knockout очень слабо похож на bacon/rx/kefir — мало в нем реактивного.

Я бы сказал наоборот, в bacon/rx/kefir мало реактивного, так как они не о data-flow, а о event-streams.


Возможно имеется в виду ситуация, когда значение в зависимой ячейке изменилось на такое же?

Нет, имеются ввиду динамические зависимости, задаваемые не в коде, а пользовательским вводом. Ну а то, что "изменение" значения на такое же требует дополнительных костылей в виде distinct — тоже о многом говорит :-)


А вот слова при «кривоватость» библиотеки настораживают… В чем кривость-то? На данный момент у RxJS самое обширное АПИ по работе с Observable.

В подходе кривость — заменяются стандартные операторы управления потоком на своё апи, превращают код в лапшу.

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

Раньше я интересовался baconjs и кефиром, у них отличная документация, картинки, все понятно, с rxjs все совсем не так, хуже во много раз, что существенно замедляет изучение и смена мышления тут не причем. Под вашу конкретную ситуацию вы будете искать подходящие операторы или их комбинацию продираясь через устаревшие и запутанные описания (эта тема в статье раскрыта). А понятий в этой библиотеке хватает.

На простых хипстерских примерах — все гуд, но как только нужно что-то с более сложной логикой — труба дело (похоже проще всего написать свой оператор, чем комбинировать). Например, нужно отслеживать все запросы к апи, в случае ошибки соединения выдать запрос (возможно несколько раз) юзеру на повторую попытку (все остальные запросы должны быть приостановлены пока юзер не ответил), надо ослеживать устаревание аксесс токена и перезапрашивать его по рефреш токену прозрачно для клиентского кода, надо выполнять некоторые действия перед запросом и после запроса. Реализация в лоб на rxjs превратит это все в кромешный ад, в котором сможет разобраться только автор, пока его память свежа. А вот на промисах я такое делал и не скажу, что вышло сложно или ненадежно.

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

Размер библиотеки при широком использовании немаленький.

По операторам/методам доки можно смотреть тут, они тоже с картинками. Да, иногда есть расхождения доков и свежей версии. Например, чтобы задать значение для throttle/debounce в миллисекундах, нужно использовать throttleTime/debounceTime. хотя в доках это пока не исправлено. Но это же всё-таки бета.


Я не большой спец по rx, но особых сложностей при использовании не испытывал. Юзкейсы в проекте похожие. В моём проекте достаточно при определённых ошибках перенаправить на страницу логина. В вашем же случае, просто будет больше действий при ошибке. Навскидку, я бы сделал как-то так:
В апи-сервисе завести observable, в который при ошибке будет что-то эмититься. В корневом компоненте подписаться на этот стрим и при ошибке выдавать юзеру окошко. Чтобы приостановить запросы, скорее всего можно использовать publish/connect. Ответ юзера отправлять обратно в api-сервис. Для этого можно завести в нём Subject и подписаться на него в самом сервисе. Далее, можно просто использовать combineLatest или что-то похожее для объединения стримов и потом фильтровать состояние приложения (была ли ошибка, что ответил юзер и т.д.). Токен можно проверять при каждом запросе к апи: если он протух, делаем запрос на новый, и в случае успеха выполняем первичный запрос. На крайняк, можно писать в "стиле промисов", т.к. Observable по-сути могут всё, что могут промисы.
Слежка за подписками упрощается, если использовать пайп async, т.к. он сам отпишется при уничтожении компонента.


Конечно, недостатки в библиотеке есть, но большинство проблем лично у меня связаны именно с недостаточным "мышлением" в реактивном стиле.

try/catch/finally/do/retry/retrywhen/map/of/flatmap/share/throw — сейчас все эти операторы/методы используются, при этом обрабатывается только часть ситуаций, добавить сюда паб/саб/комбайн и точно никто не разберется). Да в случае ошибки будет очень весело.
Sign up to leave a comment.