Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 01.02.2014, 23:59
Профессор
Отправить личное сообщение для tenshi Посмотреть профиль Найти все сообщения от tenshi
 
Регистрация: 20.03.2008
Сообщений: 1,183

$jin.atom - frp всегда с тобой
$jin.atom – компактная (4KB в сжатом виде) библиотека, предоставляющая класс для создания атомов – минимальных частиц FRP-приложений. FRP – функциональное реактивное программирование. Суть его в том, что мы описываем зависимости между данными в виде функций и рантайм автоматически поддерживает актуальность всех значений. Атом хранит в себе ровно одно значение, а также всю необходимую информацию о зависимостях (кто от него зависит, от кого он зависит, как вычислить значение и тп).

Отличительные особенности библиотеки:
• простота реализации
• удобство интеграции с другими парадигмами
• автоматическое выявление зависимостей

Общие сведения

Атомы бывают 3 типов:

Источники (sources) – атомы, которые ни от кого не зависят – их значение меняется императивно в зависимости от каких-либо внешних факторов (например, действия пользователя или пришедшие данные с сервера).

Покрывала (covers) – атомы, которые умеют не только функционально вычислять своё значение, но и императивно его куда-нибудь передавать. Такие используются как конечные звенья дерева зависимостей и как правило от них никто не зависит.

Промежуточные (transits) – все остальные атомы, существование которых обусловлено лишь необходимостью связи покрывал с источниками. Используются для меморизации промежуточных вычислений.

Зависимости между атомами строятся автоматически в момент вычисления из значений. Каждый атом хранит в себе:

• список хозяев (masters) – атомов, от значений которых непосредственно зависит значение текущего
• список рабов (slaves) – атомов, значения которых непосредственно зависят от значения текущего
• номер слоя (slice) – на 1 больше, чем номера слоёв всех хозяев, или иначе – максимальная дистанция до источников.

Каждый раз, когда реально меняется значение мастера (сравнение идёт через оператор идентичности `===`) – все рабы получают распоряжение об обновлении, но делают они это не сразу, а отложенно. Причем, чем меньше номер слоя, тем раньше будет вычислено значение атома. Это сделано для того, чтобы не приходилось несколько раз пересчитывать значение атома, пока меняются значения его хозяев.

Несколько примеров

Таймер, каждую секунду увеличивающий своё значение. Начинает работать лишь когда кто-либо запрашивает его значение.
var tick = $jin.atom(
    {   pull: function( prev ){
            setTimeout( function( ){
                tick.pull()
            }, 1000 )
            return ( Number( next ) || 0 ) + 1
        }
    })

Пример очень простой, не повторяйте это дома (как минимум не хватает возможности его остановки), но для иллюстрации подойдёт.

Отобразим текущее значение таймера в заголовке окна.
var title = $jin.atom(
    {   pull: function( ){
            return 'Current tick: ' + tick.get()
        }
    ,   push: function( next ){
            document.title = next
        }
    })
    title.update()

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

var $my = { timer: {} }
    
    $jin.atom.prop({ '$my.timer.tick':
    {   pull: function( prev ){
            setTimeout( function( ){
                $my.timer.tickAtom().pull()
            }, 1000 )
            
            return ( Number( next ) || 0 ) + 1
        }
    }})
    
    $jin.atom.prop({ '$my.timer.title':
    {   pull: function( ){
            return 'Current tick: ' + this.tick()
        }
    ,   push: function( next ){
            document.title = next
        }
    }})
    
    $my.timer.title()

Ещё один пример – простенькое слайдшоу: http://nin-jin.github.io/slide/

Альтернативы

KnockOut: http://knockoutjs.com/

− Недостатки
• Большой объём. В 10 раз больше исходного кода.
• Плохая интеграция с прототипным наследованием. Каждый observable (атом) – это отдельное замыкание. Все observable-свойства создаются в конструкторе вьюшки, а не по мере необходимости. Это потребляет лишние ресурсы.

± Спорные моменты
• В комплекте идёт функционал формирования дом-дерева и биндинга к его узлам. Это отдельный, большой функционал, который не всегда необходим.

Подробнее тут: http://hyoo.ru/?article=Атомы+на+JS;author=Jin
Собранная либа: https://github.com/nin-jin/nin-jin.g...e%3Drelease.js
__________________
.ня

Последний раз редактировалось tenshi, 14.03.2014 в 21:11.
Ответить с цитированием
  #2 (permalink)  
Старый 02.02.2014, 00:08
Профессор
Отправить личное сообщение для tenshi Посмотреть профиль Найти все сообщения от tenshi
 
Регистрация: 20.03.2008
Сообщений: 1,183

Критика очень приветствуется
__________________
.ня
Ответить с цитированием
  #3 (permalink)  
Старый 03.02.2014, 20:14
Аватар для nerv_
junior
Отправить личное сообщение для nerv_ Посмотреть профиль Найти все сообщения от nerv_
 
Регистрация: 29.11.2011
Сообщений: 3,924

Сообщение от tenshi
Суть его в том, что мы описываем зависимости между данными в виде функций и рантайм автоматически поддерживает актуальность всех значений
кто поддерживает?

Сообщение от tenshi
Каждый раз, когда реально меняется значение мастера (сравнение идёт через оператор идентичности `===`) – все рабы получают распоряжение об обновлении, но делают они это не сразу, а отложенно. Причем, чем меньше номер слоя, тем раньше будет вычислено значение атома. Это сделано для того, чтобы не приходилось несколько раз пересчитывать значение атома, пока меняются значения его хозяев.
что, если нижний слой влияет на верхний? Получаются те же самые бесконечные вычисления

Написано сложно. Зачем писать сложно о сложном? Так любой сможет. А ты попробуй просто напиши.

С FRP дела не имел.

Что я "вижу" по сути (код не глядел):

function Atom() {
   this.children = [];
}

// уведомить об изменении
Atom.prototype.notify = function() {
   // цикл по детям, детям детей и т.д.
};

// создаем родительский atom
var atom = new Atom();
// формируем цепочку детей (зависимостей)
atom.children.push(new Atom());

// какое нибудь изменение, а в след за ним уведомление
atom.notify();
или
вводим id-шники в рамках атомов, с помощью которых можно подписаться или отслеживать любые изменения любых атомов и формировать зависимости. Все
__________________
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук

Последний раз редактировалось nerv_, 03.02.2014 в 20:18.
Ответить с цитированием
  #4 (permalink)  
Старый 03.02.2014, 23:44
Профессор
Отправить личное сообщение для tenshi Посмотреть профиль Найти все сообщения от tenshi
 
Регистрация: 20.03.2008
Сообщений: 1,183

> кто поддерживает?

В данном случае представляемая библиотека

> что, если нижний слой влияет на верхний? Получаются те же самые бесконечные вычисления

Нет, будет исключение, ибо это явная ошибка в консерватории)

> Написано сложно. Зачем писать сложно о сложном? Так любой сможет. А ты попробуй просто напиши.

Это и есть просто, на сколько это возможно)

> // формируем цепочку детей (зависимостей)
> atom.children.push(new Atom());

Вот именно этого ручного связывания и хотелось бы избежать.

Смотри, пусть у нас есть такое определение свойства:

$jin.atom.prop({ '$jin.task.view.item..current':
{   pull: function( ){
        return this.list().task() === this.task()
    }
}})


Что тут происходит:
1. функция - это формула вычисления значения
2. когда кто-либо запрашиват значение атома, этот атом добавляет себя в стек слушателей и исполнятет указанную функцию для вычисления значения
3. this.list - это тоже атомное свойство, когда мы запрашиваем ее значение привязанный к ней атом смотрит в стек и связывает себя с атомом с вершины стека, таким образом атом свойства this.current становится слушателем изменений атома свойства this.list
4. this.list() возвращает объект и у этого объекта есть атомное свойство task - аналогично происходит подписка и на него.
5. Ну и в завершении происходит подписка на this.task()

Но сама прелесть в том, что подписки тоже всегда актуальны:

$jin.atom.prop({ '$jin.task.view.item..current':
{   pull: function( ){
        if( this.disabled() ) return false
        return this.list().task() === this.task()
    }
}})


Если в какой-то момент свойство this.disabled станет равно true, то при следующем вычислении this.current ее атом будет отписан от всех атомов кроме атому this.disabled, потому что пока свойство this.disabled не изменится не изменится и значение this.current
__________________
.ня
Ответить с цитированием
  #5 (permalink)  
Старый 07.02.2014, 17:18
Аватар для nerv_
junior
Отправить личное сообщение для nerv_ Посмотреть профиль Найти все сообщения от nerv_
 
Регистрация: 29.11.2011
Сообщений: 3,924

tenshi, спасибо за разъяснения
__________________
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук
Ответить с цитированием
  #6 (permalink)  
Старый 14.03.2014, 21:09
Профессор
Отправить личное сообщение для tenshi Посмотреть профиль Найти все сообщения от tenshi
 
Регистрация: 20.03.2008
Сообщений: 1,183

Кому интересно, сравнение скорости с КО: http://jsperf.com/reactive-libraries-comparison

И диаграмка распространения изменений, чтобы понять почему такая большая разница: http://nin-jin.github.io/slide/#slide=induction
__________________
.ня
Ответить с цитированием
  #7 (permalink)  
Старый 15.03.2014, 00:18
Аватар для nerv_
junior
Отправить личное сообщение для nerv_ Посмотреть профиль Найти все сообщения от nerv_
 
Регистрация: 29.11.2011
Сообщений: 3,924

Сообщение от tenshi
И диаграмка распространения изменений, чтобы понять почему такая большая разница
и чего она отражает?

Цитата:
При больших скоупах это довольно затратно. Пруфлинк http://jsfiddle.net/7sj76/8/.
Мой "пруфлинк" http://jsfiddle.net/7sj76/17/
__________________
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук

Последний раз редактировалось nerv_, 15.03.2014 в 00:50.
Ответить с цитированием
  #8 (permalink)  
Старый 12.04.2014, 17:01
Профессор
Отправить личное сообщение для tenshi Посмотреть профиль Найти все сообщения от tenshi
 
Регистрация: 20.03.2008
Сообщений: 1,183

Показывает как происходят вычисления при изменении нескольких моделей.

К чему это?
__________________
.ня
Ответить с цитированием
  #9 (permalink)  
Старый 20.04.2014, 21:17
Профессор
Отправить личное сообщение для tenshi Посмотреть профиль Найти все сообщения от tenshi
 
Регистрация: 20.03.2008
Сообщений: 1,183

Добавил мануал по реактивным вьюшкам: http://hyoo.ru/?article=%D0%A0%D0%B5...F;a uthor=Jin

Несколько оптимизировал сами атомы и добавил реализацию thenable интерфейса, что позволяет им выступать в качестве обещаний. Обещания - частный случай атомов. Обещание резолвится (или фейлится) ровно один раз. В то же время атом может менять свое состояние по мере необходимости.
__________________
.ня
Ответить с цитированием
  #10 (permalink)  
Старый 20.04.2014, 21:42
Профессор
Отправить личное сообщение для tenshi Посмотреть профиль Найти все сообщения от tenshi
 
Регистрация: 20.03.2008
Сообщений: 1,183

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

__________________
.ня
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
JS загружает изображение всегда с локального кэша - почему? buhpro Общие вопросы Javascript 4 02.10.2013 21:01
window.scroll не всегда срабатывает... prohor.zotikov Общие вопросы Javascript 0 11.12.2012 11:37
Не всегда срабатывает onclick Almiur Events/DOM/Window 7 25.11.2011 11:39