"frp — делаем жизнь проще", Трдат Мкртчян, moscowjs 19
TRANSCRIPT
4
( Плюсы (( Иммутабельные данные )( Отсутствие состояния )( Лень, как стратегия вычислений )( Частичное применение и карринг ))
Получаем декларативный подход
16
( RxJS ( Microsoft Reactive Extensions )
github.com/Reactive-Extensions/RxJS )
● Большое комьюнити
● Подробная документация
17
( Bacon ( Juha Paananen @raimohanska )
github.com/baconjs/baconjs )
● Простое API
● Ясная документация
18
( Kefir ( Roman Pominov @rpominov )
github.com/pozadi/kefir )
● Высокая производительность
● Активно развивается
19
( Warden.js ( Trdat Mkrtchyan @zeffirsky )
github.com/zefirka/Warden.js )
( ProAct ( Nickolay Tzvetinov @ntzvetinov )
github.com/proactjs/proactjs )
20
var timer;
input.addEventListener('keydown', function(event){ if(event.keyCode == 13){
clearTimeout(timer);timer = setTimeout(function(){
doSomething(); }, 500);
}});
Debounce в императивном варианте
21
Bacon.fromEventTarget(input, 'keydown').filter(function(event){
return event.keyCode == 13}).debounce(500).onValue(doSomething);
Debounce в FRP
22
События
var clicks = Bacon.fromBinder(function(sink) { document.addEventListener('click', sink);});
Ответы сервера
var responses = Bacon.fromBinder(function(sink) { $.get('path/to', sink);});
Таймер
var ticks = Bacon.fromBinder(function(sink) {setInterval(sink, 1000);
});
Все что угодно – это stream
22
23
События
var clicks = Bacon.fromBinder(function(sink) { document.addEventListener('click', sink);});
Ответы сервера
var responses = Bacon.fromBinder(function(sink) { $.get('path/to', sink);});
Таймер
var ticks = Bacon.fromBinder(function(sink) {setInterval(sink, 1000);
});
Все что угодно – это stream
23
24
События
var clicks = Bacon.fromBinder(function(sink) { document.addEventListener('click', sink);});
Ответы сервера
var responses = Bacon.fromBinder(function(sink) { $.get('path/to', sink);});
Таймер
var ticks = Bacon.fromBinder(function(sink) {setInterval(sink, 1000);
});
Все что угодно – это stream
24
25
// пусть получаем данные из сокета в форме// {player : [Object] , score : [Integer] }
var totalScore = 0;
function calculateScore(data){if(data.player.status == 'A'){
totalScore += data.score}
}
socket.on('score', calculateScore);
Вычисления в императивном стиле
26
var totalScore = socket.stream('score').filter(function (data){
return data.player.status == 'A';}).map('.score').reduce(function (sum, score){
return sum + score;})
FRP упрощает вычисления
+ Вычисления не производятся, если они не нужны
+ Состояние чистое
+ Можно переиспользовать функции-обработчики
29
Controllerapp.controller('someCtrl', function ($scope, frp){
$scope.users = [];$scope.query = '';
frp.toStream($scope, 'query').debounce(500).map('.newResult') // привет $watch.filter(notEmpty).onValue(function(query){
frp.http.get('search:users', { query: query }).assign($scope, 'users');
});}
+ В духе Angular
- Вынуждены оборачивать службы Angular в FRP
30
Controllerapp.controller('someCtrl', function ($scope, frp){
$scope.users = [];$scope.query = '';
frp.toStream($scope, 'query').debounce(500).map('.newResult') // привет $watch.filter(notEmpty).onValue(function(query){
frp.http.get('search:users', { query: query }).assign($scope, 'users');
});}
+ Никаких ng-change в разметке
- Неявные $watch и $apply
32
( Чуть больше магии, но видно,как это работает )
( Все побочные действия – это setState )
( Отлично встраивается во FLUX )
( React )
32
33
( React )
componentDidMount: function(){this.streamFrom(document, 'mousemove')
.map(function(event){return {
x: event.clientX,y: event.clientY
}}).onValue(this.setState.bind(this));
},
render: function() {return <h1> X: {this.state.x}, Y: {this.state.y}</h1>
}