$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