Solit 2014, Реактивный Javascript. Победа над асинхронностью и...

Preview:

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

Спасибо!

Виктор Русакович из Минска, Беларусь

nemiga@gmail.com

Вопросы?