RxJS: реактивное расширение для фронтенд разработки

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

    Итак, прошу любить и жаловать, Виктор Русакович. Родом из Минска, работает в компании GP Software.travel.
    Виктор последние пять лет занимается (в основном) фронт-енд разработкой. Ну а начинал, как и большинство из нас, с jQuery.

    Потом был backbone, angular v1. Последние полгода он работает в проекте на Redux/React.js (часто их путают с RxJS, но это немного другое).



    В твоем проекте активно используется реактив. Можешь объяснить, что это такое и с чего это движение началось? Сейчас у всех на слуху RxJS, RxJava, RxPython, RxBasic, ну и разве что RxBrainfuck нет.



    Действительно, один из моих предыдущих проектов был насквозь пронизан использованием библиотеки RxJS. Все AJAX-запросы, работа с DOM-событиями, порой просто обработка статичных данных — всё это проходило через RxJS.

    Для начала пара слов о «реактивном программировании» как таковом. Например, в жизни с «реактивностью» вы сталкиваетесь в Excel:



    Как видите, для автоматического вычисления суммы были созданы ячейки с формулами. На языке реактивного программирования это можно изобразить в виде двух потоков данных, gross и taxes, и третьего потока net, который будет собирать данные из gross и taxes и аггрегировать их по нашей формуле, превращая в итоговое значение.
    Я буду пользоваться очень удобным онлайн-редактором, чтобы показывать все свои примеры. Этот редактор хорош тем, что прямо в брузере рендерит результат работы приложения. Ну и самая классная фича радактора в том, что потом все сниппеты останутся доступными по прямой ссылке. Мой первый пример тут.

    var gross = Rx.Observable.just(5000)
    var taxes = Rx.Observable.just(13)
    var net = gross.combineLatest(taxes, (g, t) => g - t / 100 * g)
    
    net.subscribe(money => document.getElementById('out').value = money)
    


    В интернете можно найти много вариантов определения «реактивного программирования»: в Википедии, здесь, на Хабре. Тема серьёзная, даже существует специальный манифест, который можно подписать, если вы согласны с его идеями — только зачем? Лично мне все эти определения не нравятся, и я придумал свое:
    Реактивное программирование — это когда ты вместо обработки событий по одному объединяешь их в поток и затем работаешь уже только с ним.
    (Дата, Подпись, Печать).




    Если же говорить о том, как все это зародилось, то история появления RxJS такая.
    Году в 2010-2011 ребята из Microsoft, которые работали c .NET, решили, что неплохо было бы и для JS сделать реактивную библиотеку. Дело в том, что в .NET уже достаточно давно был популярен LINQ. Например, вот так с помощью LINQ можно подсчитать количество вхождений определенного слова в строке.

           string searchTerm = "data";
           //Convert the string into an array of words
            string[] source = text.Split(new char[] { '.', '?', '!', ' ', ';', ':', ',' }, StringSplitOptions.RemoveEmptyEntries);
            // Create the query.  Use ToLowerInvariant to match "data" and "Data" 
            var matchQuery = from word in source
                             where word.ToLowerInvariant() == searchTerm.ToLowerInvariant()
                             select word;
            // Count the matches, which executes the query.
            int wordCount = matchQuery.Count();
    


    Нам посчастливлось начать работать с самой первой версии библиотеки. Код тогда ещё был не в GitHub, а в каком-то собственном репозитории от Microsoft. Кроме того, особенности лицензии не позволяли включать в проект неминифицированную версию. Огромной проблемой была документация — приходилось читать статьи, написанные для .NET, и пытаться понимать на уровне концепций, невзирая на различия в языках. Именно тогда я понял, что могу писать на любом языке программирования.

    Как вообще вы пришли к идее использовать RxJS?



    В каждом проекте есть свои особенности, которые влияют на выбор той или иной технологии. Решение может быть обсуловленно сложностью интерфейса, наличием или отсутствием внятного Roadmap’a, который показывает, что проект будет развиваться и усложняться как минимум несколько лет. А ещё в 2012 мы не смогли найти ничего другого, похожего на RxJS. Нам была важна хорошая техническая поддержка, и мы поддались бренду Microsoft.

    Зачем же нам понадобился RxJS в тот момент?
    Представьте себе приложение, в котором много независимых UI компонентов.
    Теперь добавляем сюда штук 10 API запросов (от авторизации до проверки цены продукта). Так, есть.
    Затем группируем компонеты во View так, что некоторые используются в разных View, а некоторые — нет. Хорошо получается.

    Теперь нам надо научить компоненты менять свое состояние между запросами. Где-то показать слой со спиннером, где-то спрятать данные, когда вернется ошибочный запрос. Причём, компонент может зависить от нескольких API и, более того, иногда имеет значение не одиночный запрос, а серия запросов и их ответы. И вишенку на наш торт — на улице 2012 год, Angular нету, Backbone не подходит — значит, будем работать только с jQuery. Без фреймворка.

    RxJS подошел идеально. Мы перестали ловить отдельные события и строить огромные пирамиды callback’ов. Что такое пирамида из callback объяснять не нужно, надеюсь?

    Вместо этого между компонентами у нас появились потоки с данными, из которых мы могли создавать новые потоки, нужные в конкретной ситуации. Например, есть у нас поток с выбором какого-то продукта. Если пользователь добавляет что-то в корзину, то в этом потоке появляется событие с добавленным продуктом. Если пользователь удаляет продукт — в потоке появляется событие с пустым массивом. Допустим, мы хотим, чтобы таблица моргала красным каждый раз, когда удаляется что-то из корзины:

    var cart = require(‘streams/cart’)
    var removalsFromCart = cart
      .bufferWithCount(2,1)  
      .where(history => history[1].length < history[0].length)
    
    removalsFromCart.subscribe(() => $(‘table’).setClassFor1Sec(‘warning))
    
    

    Ссылка на сниппет.

    .bufferWithCount() будет брать предыдущее и текущее события и передавать их массивом дальше. В .where() мы проверяем, что текущая корзина (она будет вторым в массиве после .bufferWithCount) содержит меньше элементов, чем предыдущая. Если условие правдивое, то данные передаются дальше в метод .subscribe(), попасть в который является целью каждого события в потоке. Если б не использование метода .where(), то каждое изменение корзины вызывало бы моргание.

    Значит, причины были именно такие. Как ты считаешь, а по каким еще причинам имеет смысл начинать использовать реактивный подход в проектах?



    В самом начале мы все-все события превращали в Rx.Observable. Со временем мы поняли, что это не всегда оправдано. Например, если мы на 100% уверены, что клик по этой кнопке вызывает перезагрузку страницы, то никаких дополнительных изменений на странице не потребуется. Теперь такие простые и не связанные с другими события мы обрабатываем без RxJS.

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

    Но вот за что мы особенно полюбили RxJS, так это за то, что при написании всех этих .where(), .buffer(), .map() и пр. методов нам совершенно не важно, что является источником данных. Это может быть массив или же вебсокет, который может прислать по 100 объектов в секунду, а потом вдруг остановиться на минуту. Все эти события равны для нас и обрабатываются одинаково.

    А это значит, что, разобравшись с асинхронными вещами, мы можем начать использовать RxJS для обработки абсолютно синхронных данных, например, пометить жирным все внешние ссылки:
    Rx.Observable.from(document.querySelectorAll('a'))
      .where(el => el.getAttribute('href').indexOf('http') === 0)
      .subscribe(el => el.style.fontWeight = 'bold')
    


    Ссылка на сниппет.
    Резюмируя, скажу, что, как мне кажется, RxJS идеально подойдёт для проектов с большими потоками данных, на которые нужно реагировать — игры, чаты и простые сайты, в которых есть какие-то сложные обновления на странице. Не зря, думаю, RxJS включили в Angular 2.

    А какие еще решения есть для JS?



    Сходу вспоминается несколько альтернатив. В первую очередь это BaconJS. Библиотека создана примерно в то же самое время, когда и RxJS. Что интересно, в одном из проектов мы даже пытались выбрать между RxJS и Bacon — это было года 4 назад, когда обе библиотеки только-только вышли. Но тогда выбор склонился в сторону RxJS, так как Bacon проигрывал по количеству стандартных методов и более простому API в целом. Ну, и еще одним немаловажным фактором, как я уже сказал, было то, что развитием и поддержкой занимался лишь Juha Paananen, а за RxJS стояла Microsoft. Сегодня пользоваться BaconJS можно без каких-либо опасений, потому что хорошее сообщество уже сформировалось, API хорошо задокументировано, и можно найти много отличных примеров.

    Следующая альтернатива — это KefirJS (в языке еще остались слова, к которым не добавили JS? :). Замечательная библиотека для реактивного программированния, поддерживаемая нашим соотечественником Романом Поминовым. Роман создавал KefirJS, стараясь взять простоту API от BaconJS (по сравнению с RxJS) и сразу исправляя ошибки в производительности. И знаете, получилось хорошо! Мы, например, пользуемся кефиром каждый день в одном из проектов.

    Неужели всё так радужно? Можо просто взять и использовать эти фреймворки?



    Есть нюансы.
    Года 3 назад после доклада про RxJS на конференции у меня спросили: «Правильно ли я понял, что для того, чтобы использовать реактивное программирование, нужно изменить образ мышления?» Это был самый правильный вопрос за все мои доклады! Очень часто он читался в глазах слушателей, но задали его только раз. И ответ на него: «Да!»

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

    Вторая проблема состоит в том, что порой сложно понять, какой именно метод можно применить в конкретном случае. Например, вы превратили какие-то скалярные данные в запросы. Как теперь получить доступ к ответу сервера? Допустим, вы прочитали описание всех методов в RxJS и даже запомнили .switch(): «Transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence»…
    Ну вы поняли — без jsfiddle не разобраться.

    С этим сложно бороться, надо будет пережить момент, пока знаний API недостаточно для безостановочного кодинга. Тут пригодится проект rxmarbles.com — его автор (André Staltz) создал большое количество динамичных временных диаграмм, по которым можно проследить, как именно влияет порядок данных на итоговый поток. Однако, метода .switch() там не нашлось.

    К счастью есть reactivex.io/documentation/operators/switch.html, где можно найти подробное и человеческое описание основных методов и их временных диаграмм, и switch() в том числе:



    Видно, что метод .switch() возвращает данные из вложенных потоков. Причем, что важно, данные из вложенного потока поступают на выход до тех пор, пока не появляется новый вложенный поток, который скоро начнет производить данные: обратите внимание на то, что желтый круг не прошел, хотя желтый треугольник еще не появился.
    Именно этот метод мы обычно используем, когда работаем с ajax — как только отправлен новый запрос, данные, которые могут вернуться из предыдущего запроса, нам не интересны. Теперь мы никогда не увидим устаревший ответ сервера.

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



    Сегодня ресурсов очень много. Только ресурсов со ссылками на ресурсы десятки, на все вкусы: кто-то любит видео-курсы, кто-то — чтение документации с примерами, кому-то книги подавай. Я бы начал вот с этого списка:

    • egghead.io/series/introduction-to-reactive-programming — отличный вводный курс от автора RxMarbles;
    • reactivex.io/intro.html — много теории от авторов RxJS;
    • www.reactivemanifesto.org — манифест (куда без него?);
    • xgrommx.github.io/rx-book — много документации из репозитория RxJS, но есть и много своих хороших статей, например про Backpresure в RxJS (а это, между прочим, часть манифеста!);
    • www.introtorx.com — сборник статей, которые вошли в одноименную книгу (на сайте есть бесплатная mobi версия для Kindle). Не пугайтесь, что всё на .NET — отличное чтение, чтобы понять концепцию. Да и .NET очень похож на TypeScript;
    • Статьи по тегу FRP на Хабре;
    • А начать я советую с подключения в проект rx.lite.js и переводе ваших Ajax (Fetch) запросов на Rx.Observable.




    Надеюсь, что в вас пробудилось желание выбросить императивный подход и начать наконец следовать реактивной парадигме! Хватит тянуть данные! Пусть данные сами пройдут по созданым цепочкам!
    Кстати, доклад для holyjs.ru уже готов, остались небольшие доработки. В докладе я расскажу на примерах о практическом применении RxJS в ваших проектах. Так что увидимся в Петербурге на конференции 5-ого июня!
    JUG.ru Group 1 354,02
    Конференции для взрослых. Java, .NET, JS и др. 18+
    Поделиться публикацией
    Комментарии 50
    • 0
      Реактивное программирование — это когда ты вместо обработки событий по одному объединяешь их в поток и затем работаешь уже только с ним.

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

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

          Расскажите это Рональду Куну, автору (одному из) книги 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.


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

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

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

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


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


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

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

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

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

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

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


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

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


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

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

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

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


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

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


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

                          • 0
                            Хватит уже изворачиваться, «flow» переводится конкретно как «поток».
                            • +2

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


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

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

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


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

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

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

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

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

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

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

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

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

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


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

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


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

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

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

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


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

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

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

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


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

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

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

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

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

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

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

                                    • 0
                                      Попробуем завтра подправить. Сегодня редакторы спят. Так сказать, подписались на поток сновидений.
                              • 0
                                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

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

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

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

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


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


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

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

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

                      • 0
                        Я прочитал эту фразу с акцентом на повышении уровня абстракции. Т.е., автор, как мне показалось, говорит о наведении порядка в изменении состоянии приложения путём организации разрозненных обработчиков событий в декларативно обозначенные (хоть акторами выражено, хоть атомами, хоть трансдюсерами) _потоки_ распространения изменений состояния приложения. (Но я, наверное, наконец смог прочитать эту фразу и по-вашему.)
                    • 0
                      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.
                      • +1
                        Сильно много «реактивно» на Knockout не попишешь — не хватает доступных методов для работы с observable — filter/combineLatest/flatMap. Да и по виду код Knockout очень слабо похож на bacon/rx/kefir — мало в нем реактивного


                        эти методы — буква F в FRP, отсутствие этих методов делает Knockout менее функциональным, но никак не менее реактивным. К тому же без них вполне неплохо живётся, просто вместо a.add(b) пишется a+b и необходимость в изучении 100500 методов на все случаи жизни сразу отпадает.
                    • 0
                      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.

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

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

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

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

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

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

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


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


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

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

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

                        Самое читаемое