Upload
solit
View
175
Download
7
Embed Size (px)
DESCRIPTION
Виктор Русакович, Минск, Web-developer c 6-ти летним опытом разработки, компания GP Software.travel «Реактивный JavaScript. Победа над асинхронностью и вложенностью». Development секция. Для разработчиков. Высокий уровень подготовки. «Непрерывная интеграция сложного проекта. Кто всё сломал?». IT секция. Agile отделение. Для всех уровней подготовки.
Citation preview
Реактивные расширения для
JavaScript......и пусть события сами идут
Где мы используем события?
ВЕЗДЕ
Что не так с событиями?
● как компоновать?● как фильтровать?● как собирать данные от разных
событий?● как не сойти с ума от
асинхронности?● как не строить Пирамиды Обратных
Вызовов?● … (еще примерно 42 пункта)
О ужасный callback!
$('#some-element').animate({ width: 100, height: 50}, { duration: 300, complete: function() { // Step 2: $('#another-element').fadeOut(300, function() { // Step 3: setTimeout(function() { // Step 4: $.ajax({ url: 'http://google.com', }).done(function() { // Step 5: setTimeout(function() { // Step 6: $('#third-element').remove(); }, 250); }); }, 300); }); }});
О ужасный callback!
step1(function (value1) { step2(value1, function(value2) { step3(value2, function(value3) { step4(value3, function(value4) { // Do something with value4 }); }); });});
О ужасный callback! (и доступ к данным)
step1(function (value1) { step2(value1, function(value2) { step3(value2, function(value3) {
if (value2 === 4) return null step4(value3, function(value4) { if (value2 > value3) work() }); }); });});
Варианты решения а-ля promise...Q.fcall(step1) .then(step2) .then(step3) .then(step4) .then(function (value4) { // Do something with value4 }) .done();
https://github.com/kriskowal/q/
A tool for making and composing asynchronous promises in JavaScript (282 362 forks at Github).
...или настоящими promise
function successFunc(){ console.log( “success!” ); }
function failureFunc(){ console.log( “failure!” ); }
$.when(
$.ajax("/main.php" ),
$.ajax("/modules.php" ),
$.ajax(“/lists.php” )
).then( successFunc, failureFunc );
Все равно не хватает контроля над данными!
Reactive Extensions for JS
...is a library to compose asynchronous and event-based programs using
observable collections and LINQ-style query operators.
Реактивные расширения
...библиотека для создания асинхронных и основанных на событиях программ используя
наблюдаемые коллекции и LINQ операторы.
Состав RxJS:
● объекты для представления асинхронных потоков данных
● операторы для создания, фильтрации и управления подобными объектами и данными внутри них
● немного магии
Реактивное программирование
- парадигма, ориентированная на потоки данных и распространение изменений.
Это означает, что должна существовать возможность легко выражать статические и динамические потоки данных, а также то, что выполняемая модель должна автоматически распространять изменения сквозь поток данных.
2 примера реактивности из жизни1. Excel 2. Ищем работу:
обзванивать вакансииpull vs
разместить резюме ждать звонков
push
Основа идеологии
НаблюдательObserver
Наблюдаемое (поток)
Observable
подписывается (subscribe) ⇒ ⇐ присылает сообщения (onNext)
Наблюдаемая последовательнсть
RxJS против не-RxJS
$
('button').clickAsObserva
ble()
.skip(2)
.take(2)
.timeInterval()
.doAction(flickerButton)
.subscribe(logClicks)
var i= 0, lastTs = new Date(), nowTs$('button').click(function(){ i++; if (i<=2 || i>=4) return nowTs = new Date() flickerButton() logClicks(diff(lastTs, nowTs))})
query
Используем привычные функции
Array● concat● every● filter● map● reduce● some● forEach
Observable● concat● all● where● select● aggregate● any● subscribe
Поддержка родных событий jQuery•bind - bindAsObservable
•delegate - delegateAsObservable
•live - liveAsObservable
•on - onAsObservable
•$.Deferred.toObservable /
Rx.Observable.toDeferred
Мышь и клавиатура
•change - changeAsObservable
•click - clickAsObservable
•dblclick - dblclickAsObservable
•focus - focusAsObservable
•focusin - focusinAsObservable
•focusout - focusoutAsObservable
•hover - hoverAsObservable
•keydown - keydownAsObservable
•keypress - keypressAsObservable
•keyup - keyupAsObservable
•load - loadAsObservable
•mousedown - mousedownAsObservable
•mouseenter - mouseenterAsObservable
•mouseleave - mouseleaveAsObservable
•mousemove - mousemoveAsObservable
•mouseout - mouseoutAsObservable
•mouseenter - mouseenterAsObservable
•mouseleave - mouseleaveAsObservable
•mousemove - mousemoveAsObservable
•mouseout - mouseoutAsObservable
Ajax и анимация
• ajax - ajaxAsObservable
• get - getAsObservable
• getJSON - getJSONAsObservable
• getScript - getScriptAsObservable
• post - postAsObservable
• animate - animateAsObservable
• fadeIn - fadeInAsObservable
• fadeOut - fadeOutAsObservable
• fadeTo - fadeToAsObservable
• fadeToggle - fadeToggleAsObservable
• hide - hideAsObservable
• show - showAsObservable
• slideDown - slideDownAsObservable
• slideToggle - slideToggleAsObservable
• slideUp - slideUpAsObservable
Состав RxJS:
● объекты для представления асинхронных потоков данных
● операторы для создания, фильтрации и управления подобными объектами и данными внутри них
● немного магии
Операторы объединения
● Amb● Merge● Concat● CombineLatest● Zip● Repeat
stream y
stream x
resultresult = fn(x, y, …, n)
Операторы фильтрации и выбора● where (filter) / whereTrue / whereFalse● take / takeUntil / takeWhile● skip / skipUntill / skipWhile● select (map) / selectMany /
selectProperty
stream x
resultresult = fn.apply(x)
Операторы для данных
● scan● count● min● max● groupBy● distinct / distinctUntilChanged● throttle
Временные диаграммы
Временные диаграммы вживую
j q ut
result = source.throttle(1500)
jqu
jqu
source
resultjqu
jque
jque
1,5 сек 1,5 сек
0,3 сек
.select()negativeStream = stream.select(function(value) { return -value });
negativeStream = stream.select(invert);function invert(value) { return -value });
.where()negativeStream = stream .where(function(value) { return value % 2 == 0 });
negativeStream = stream.where(isEven);
xStream.merge(yStream)
xStream.combineLatest(yStream)
x.zip(y)
.bufferWithTime(3000)
.bufferWithCount(3)
.bufferWithTimeOrCount(3000, 3)
dots.delay(1000)
Операторы - over 9000aggregateallambandanyasObservableaveragebufferbufferWithCountbufferWithTimebufferWithTimeOrCountcase | switchCasecatch | catchExceptioncatch | catchExceptioncombineLatestconcatconnectcontainscountcreatecreateWithDisposabledefaultIfEmptydeferdelaydelayWithSelectordematerializedistinctdistinctUntilChangeddo | doActiondoWhileelementAtelementAtOrDefaultempty
everyexpandfilterfinally | finallyActionfindfindIndexfirstfirstOrDefaultflatMapflatMapLatestfor | forInforkJoinfromArraygenerategenerateWithAbsoluteTimegenerateWithRelativeTimegroupBygroupByUntilgroupJoinif | ifThenignoreElementsintervalisEmptyjoinlastlastOrDefaultmanySelectmapmaxmaxBymergemergeObservablemin
minBymulticastneverobserveOnonErrorResumeNextonErrorResumeNextpluckpublishpublishLastpublishValuerangereducerefCountrepeatrepeatreplayretryreturn | returnValuesamplescanselectselectManyselectSwitchsinglesingleOrDefaultskipskipLastskipLastWithTimeskipUntilskipWhilesomestartstartWith
subscribesubscribeOnsumswitch | switchLatesttaketakeLasttakeLastBuffertakeLastBufferWithTimetakeLastWithTimetakeUntiltakeWhilethrottlethrottleWithSelectorthrow | throwExceptiontimeIntervaltimeouttimeoutWithSelectortimertimestamptoArraytoAsyncusingwhenwherewhile | whileDowindowwindowWithCountwindowWithTimewindowWithTimeOrCountzip
Языки с библиотеками RX
1.node.js2. Java3.Ruby4.Python5.ObjectiveC6.C++7. ...8.https://github.com/Reactive-Extensions
Демо
закрепим в jsFiddle полученные знания
Большое приложение
основанное на RxJS
selectedItem => {id: 1, title: “Матрешка”}loadPanelStatus => {status: true}pageSelection => {page: 4}
loadPanelStatus => {status: true}couponChanges => {id: undefined}itemRemoval => {id: 1}
Модуль корзины
Модуль каталога
КонтролерselectedItem {id: 1}
selectedItemloadPanelStatus
couponChangesitemRemoval
inputstreams
itemRemoval {id: 2}
Модуль корзины
Модуль каталога
Контролер
couponChanges .selectPropert(‘id’) => {id: “couponId”} => “couponId” .select(affectedItems) => “couponId” => [1,2,4] .subscribe(updateItemsPrice) itemRemoval .skipUntil(loadPanelStatus.whereTrue()) .takeUntil(loadPanelStatus.whereFalse()) .subscribe(markItemAsRemoved) <= {id: 1}
function affectedItems(coupon) { return $(‘.items.’+coupon).map(function(item){ return $(item).data(‘id’) }) }
selectedItem {id: 1}
selectedItemloadPanelStatus
couponChangesitemRemoval
inputstreams
itemRemoval {id: 2}
Вредные советы
● одноразовые потоки● потоки-сироты● превращаем все события в потоки,
все!● общие названия потоков (dataStream)● свои операторы не читая
документацию
Полезные советы
● простые данные в сообщениях● говорящие названия для потоков● простая логика для объединения● промежуточные потоки● функциональный подход
Фото кода
Литература:
1. http://www.introtorx.com/Content/v1.0.10621.0/00_Foreword.html
2. https://github.com/Reactive-Extensions
3. https://github.com/Reactive-Extensions/RxJS/tree/master/doc
4. http://blogs.microsoft.co.il/blogs/bnaya/archive/2010/02/25/rx-for-beginners-toc.aspx
5. http://www.slideshare.net/mattpodwysocki/cascadiajs-dont-cross-the-streams
6. http://www.slideshare.net/panesofglass/rx-workshop
7. http://www.infoq.com/reactive-extensions/
8. http://mywebbasedcomputer.com/users/johngslater/tech/rx/bubbleDiagrams.html
9. https://github.com/Netflix/RxJava/wiki/Observable-Utility-Operators
10. Matthew Podwysocki
Вопросы?