Как стать автором
Обновить

Комментарии 58

Глядя на содержимое репозитория и на скорость обновления компонентов: nin-jin мсье действительно профессиональный велосипедист и знает толк… Снимаю шляпу.
Сама концепция мне чем-то напоминает подход Reflux. Только есть еще дополнительные фичи. Где $jin.atom.prop это Reflux.store, а $jin.method это Reflux.action. Интересно, можно ли эту схему использовать для резолва моделей на бекенде, которые ожидают загрузки данных от других моделей.
Конечно можно, я активно это использую как на клиенте так и на сервере. Правда $jin.atom не очень дружит с node-fibers.
Альтернатив у TypeScript три: есть еще AtScript
Покажите мне хотя бы один репозиторий на github, написанный Atscript, кроме Atscript-playground. А пока только еще один buzz-word, наравне с другими *script (coffee, live, pure).
НЛО прилетело и опубликовало эту надпись здесь
А название статьи не смутило?
Вот еще очень интересный подход использования реактивных патернов в MV* staltz.com/mvi-freaklies/
На мой взгляд, MVP лучше справляется с задачей разделения приложения на слои.
TypeScript — это практически тот же JavaScript, но с классами, выведением типов и лямбдами.
В JS есть классы и лямбды.
Логически, безусловно есть, но синтаксически их нет.
Здрасьте. Как нет? Вот вам лямбда (у неё нет имени):

alert(1 + function() { return 1 });

Вот объект (у него есть собственный контекст):

new function() { alert(this) }
Лямбда — не просто анонимная функция, у неё есть ещё ряд свойств: ru.wikipedia.org/wiki/%D0%9B%D1%8F%D0%BC%D0%B1%D0%B4%D0%B0-%D0%B2%D1%8B%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F

Про объекты я и не спорю. Но классов синтаксически (чтобы прямо так и написать «объявляю такой-то класс с такими-то полями») — нет. Безусловно на логическом уровне классы всё же есть.
Итак, каких же свойств лямбд не хватает в JS? Из ссылки, которую вы мне дали, мне выводов об отсутствии чего-либо сделать не удалось.

Ну ок, пусть синтаксически классов там нет, не понимаю почему это плохо.
Плохо это исключительно отсутствием вменяемого автодополнения в IDE. И если тот факт, что «положенные» в объект свойства потом можно в нем же найти, IDE запомнить еще может — то на чем-то вроде атомов, где объект одного и того же типа (атом) может содержать произвольные значения, автодополнение станет совершенно бесполезным.
Давайте для начала с лямбдами разберёмся. Что с ними не так?
С лямбдами все так, особенно в последней редакции.
С лямбдами с самого начала так всё было. Теперь, что касается классов. Изначально я не согласился с утверждением, что их нет, с этой точкой зрения я готов спорить и дальше. Что какое-то IDE не умеет что-то там подсказывать в языке, мне на это наплевать, если честно. Я за точность формулировок.

И фраза «TypeScript — это практически тот же JavaScript, но с классами, выведением типов и лямбдами» попросту лжёт в отношении JS. В JS есть и классы, и лямбды, да и выведение типов, кстати, тоже есть (что-то я первый раз пропустил этот момент).
Я думаю имеется в виду подобное описание интерфейса:
github.com/borisyankov/DefinitelyTyped/blob/master/node/node-0.11.d.ts
Которое позволяет максимально точно дополнять код с учетом типа аргументов и подсказыванием какие именно аргументы ожидаются на вход.
Имеются ввиду вот такие клёвые штуки:
image
Согласен, тезис про короткую запись в виде одного выражения находится в разделе .NET. Но в JS меня всё же смущает, что this начинает указывать в стратосферу. Лямбды на мой взгляд должны быть полностью эквивалентны выполнению того же кода без создания анонимной функции.

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

Ключевые отличия: из-за многопоточной природы c# нельзя так просто сначала обновить все затронутые атомы — а потом разрешить продолжить работу. Поэтому приходится сначала переводить в состояние DIRTY все потенциально затронутые атомы — а потом уже разбираться, какие из них надо обновлять, а какие нет.

Зато есть и преимущества: наличие в языке слабых ссылок позволяет сборщику мусора собрать неиспользуемые атомы, в то время как в Js надо уничтожать атомы явно.
Потенциально затронутые — это включая те, что зависят от данного атома даже через промежуточные? Без этого точно никак во многопоточной среде?

Это да, мягкие ссылки бы капитально упростили реализацию.
Фактически, надо сделать выбор между однопоточным пересчетом всех измененных атомов — или многопоточным, но с предварительной расстановкой состояния DIRTY. Второй может оказаться как быстрее, так и медленнее, в зависимости от особенностей приложения. Я еще подумаю, можно ли как-нибудь безболезненно между этими режимами переключаться.
Зачем? Пусть первый обратившийся к атому поток начнёт его пересчёт, пометив, что он пересчитывается и другим потокам нужно просто подождать. А когда идёт реактивное обновление — аналогично. Один поток пересчитывает, а другие ждут. При этом каждый атом может пересчитываться разными потоками. Или я опять что-то не догоняю и лучше всё же дождаться статьи?
В таком случае не удается добиться согласованности атомов.

Допустим, один поток мы вычисляет атом A, при этом он обратился к атому B, который неявно зависит от C. Атом C изменился — но B об этом еще не знает, поэтому A получает старое значение B.
Теперь атом A запросил значения атома D, который тоже зависит от C, но уже успел измениться. Итог — на вход атому A поступили несогласованные друг с другом значения атомов B и D.

Поэтому надо либо выполнять обновление всех атомов «снизу», запрещая любые посторонние вычисления состояний атомов во время процесса пересчета (однопоточная модель, как сделано у вас) — либо немедленно уведомлять «верхние» атомы о возможных изменениях (как сделано у меня). Третьего пути нет.
На самом деле, в синхронном варианте есть та же проблема — если есть два пути (длинный и короткий) и мы вычисляем атом, то по одному пути мы получим актуальное значение, а по другому возможно устаревшее. Но каскадно помечать все ведомые атомы грязными — как-то брутально. В идеале было бы при вычислении атома быстро определять, что среди ведущих на есть неактуализированные и сначала вычислить их и все промежуточные до текущего, а потом уже продолжать вычисление текущего.
Если мы делаем обновление атомов снизу — то эта проблема решаема. Достаточно запретить возвращать значение атома, пока существуют невычисленные атомы ниже уровнем.

PS вы что, не так сделали?
Разумеется так, но бывают случаи когда кто-то сразу ломится в далекий ведомый атом, до которого обновления ещё не дошли и получается такая фигня.
Достаточно запретить возвращать значение атома, пока существуют невычисленные атомы ниже уровнем.
Ну да. Только как быстро определять что есть невычисленные низкоуровневые атомы?
Как-как, по номеру уровня же…
По номеру уровня мы найдем вообще все атому ниже уровнем, а не те, что хоть как-то влияют на текущий.
А нам и не требуется находить строго влияющие на текущий. Нам же в любом случае вычислять все атомы — так какая разница, в каком порядке это делать?
Тогда у нас KO может получиться с немедленным обновлением всех зависимостей.
Тогда встречный вопрос: в каком вообще случае может понадобиться не обновлять атом?
Только в одном случае — когда не все новые изменения пропушены в ведущие атомы. В данном случае это достаточно редкий кейс, да.
PS я не говорю, что атомы должны обновляться немедленно после изменения одного из них — они должны обновляться либо при простое — либо при добавлении нового атома.
Привет, Дима!
Черт, Валя меня опередил :)
Привет, Валя, давай сольём наши кармы личной перепиской в комментах! :-)
А как вы добились что из такой функции автоматом вычисляется от кого зависит атом в данный момент?

function( ) {
            if( config.get() ) {
                return 'Mouse coords is ' + coords.get()
            } else {
                return 'Mouse target is ' + target.get()
            }
        }
Предположу что так: если вызвать этот метод, то при этом вызовутся 2 из 3-х влияющих метода, например «config.get() -> coords.get()», после чего строим «зависимость» от этих 2-х. После того как config.get() изменит значение, происходит очередной вызов этого метода и обработка может пройти через «config.get() -> target.get()», — меняем зависимость на эти два, и т.д.
Т.е. после каждого изменения значения в атомах от которых зависит текущий, вызывается текущий атом для вычисления значения и при этом проверяются зависимости и перестраиваются при необходимости.

Подобным образом работает (работал в 1.x) ko.computed из Knockout.js. Это создает много работы, и может проигрывать по скорости методу «dirty checking» который используется в Angular.
Зря вы предполагаете — если бы вы прочитали прошлую статью автора, то совершенно точно знали бы, что так оно и есть.
В текущей версии KO мало что на эту тему поменялось. Там действительно делается много работы, особенно при массовых обновлениях данных. В Angular, однако, не меньше — digest может прогоняться по многу раз до стабилизации состояния. Схематично я изобразил это тут: nin-jin.github.io/slide/#slide=induction
Видимо мне стоило более явно выделить ссылку на предыдущую статью, где разбираются основные принципы работы атомов: habrahabr.ru/post/235121/

Если вкратце — в глобальной переменной $jin.atom.currentSlave сохраняется текущий вычисляемый атом и когда у другого атома запрашивают значение — он смотрит в эту переменную и связывает себя с ним.
Тут важно иметь ввиду ограничения обещаний:
1. обработчик вызывается отложенно


Вопрос, что в этом плохого? Как работает ваша реализация атомов? Изменения распространяются синхронно без отложенных вызовов?
Ничего, просто это надо иметь ввиду. В моей реализации notify вызывается синхронно, но при инвалидации атомы обновляют свои значения отложенно.
vintage Будет ли нормальная документация с примерами?
Будет полностью другая реализация :-)
ждемс =)
Что скажете про это? Похоже тоже атомы) github.com/ds300/havelock
Это тот же KnockOut, только на ES6. Всё построено на замыканиях, а значит будет большое потребление памяти и тормоза от GC при большом числе атомов. Обновления происходят немедленно, а не отложенно (чтобы отложить обновление приходится заворачивать код в транзакцию, но это частичный костыль). Динамичесное изменение зависимостей я так понял реализуется лишь через спец-функции типа ifThenElse, or, and, not.
Так и не смог сделать адекватную реализацию автосоздания субклассов с поддержкой статической типизации. Всё, что получается — либо теряет информацию о типах, либо требует писать много типового кода. Так что в следующей версии атомов тоже будут замыкания.
что там с вашим атомом?) просто вменяемой доки нет
Вылизываю :-) Надо ещё идею свойств доработать, чтобы компоненты удобно создавать.

Riim так и не обновил результаты бенчмарков :-(
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации