响应式编程及框架
DESCRIPTION
Largely inspired by (and stealed from) Erik's talk at Mix10.TRANSCRIPT
Reactive Programming and FrameworkZhao Jie - SNDA - Sep. 2010
About me
赵劼 / 老赵 / Jeffrey Zhao / 赵姐夫
日写代码三百行,不辞长作程序员
Blog: http://blog.zhaojie.me/
Twitter: @jeffz_cn
F#, Scala, JavaScript, Python, .NET, mono...
Java (as the language) hater
What?
Fundamentally change the way you think about coordinating and
orchestrating asynchronous and event-based programming
Why?
Because the essence ofCloud, Web, Mobile
is asynchronous computations
How?
By showing that asynchronous and event-base computations are just
push-based collections
Interactive & Reactive
a := b + c
Environment
Program
Interactive Reactive
Let’s implement both interfaces with...
Enumerable Collections
interface IEnumerable<out T>{ IEnumerator<T> GetEnumerator();}
interface IEnumerator<out T>{ bool MoveNext(); T Current { get; }}
EnumeratorGetEnumerator()
1
MoveNext()2
true/falseConsumer pulls
successive elements from the collection
3
Current
interface IEnumerable<out T>{ // void -‐> enumerator IEnumerator<T> GetEnumerator();}
interface IObservable<out T>{ // observer -‐> void IDisposable Subscribe(IObserver<T> o)}
Dualize Enumerable Collections
Dualize Enumerable Collections
interface IEnumerator<out T>{ bool MoveNext(); // void -‐> bool T Current { get; } // void -‐> T // throws Exception}
interface IObserver<in T>{ void OnCompleted(bool done); // bool -‐> void T Current { set; } // T -‐> void void OnError(Exception ex); // accepts Exception}
Observable Collections
interface IObservable<out T>{ IDisposable Subscribe(IObserver<T> o)}
interface IObserver<in T>{ void OnCompleted(); void OnNext(T item); void OnError(Exception ex);}
ObserverSubscribe(o)
1
OnCompleted()
3
XProducer pushes
successive elements from the collection
2
OnNext(e)
IEnumerable & IEnumerator areprototypical interfaces for interactive collections and interactive programs.
IObservable & IObserver are prototypical interfaces for observable collections and reactive, asynchronous & event-based programs.
Iterator
Observer
Iterator / ObserverPatterns in Java
class Observable { void addObserver(Observer o);
void deleteObserver(Observer o); void deleteObservers(); int countObservers(); void notifyObservers(); void notifyObservers(Object arg); void setChanged(); void clearChanged(); boolean hasChanged();}
interface Observer { void update(Observable o, Object arg);}
interface Iterable<T> { Iterator<T> iterator();}
interface Iterator<T> { T next();
boolean hasNext(); void remove();}
LINQ to Observable
If you are writingLINQ or declarative codein an interactive program...
LINQ to Observable
If you are writingLINQ or declarative codein an interactive program...
You already know how to use it!
LINQ to Observable
... the principle we go by is, don't expect to see a particular concurrency model put into C# because there're many different concurrency model ... it's more about finding things are common to to all kinds of concurrency ...
- Anders Hejlsberg
... the principle we go by is, don't expect to see a particular concurrency model put into C# because there're many different concurrency model ... it's more about finding things are common to to all kinds of concurrency ...
- Anders Hejlsberg
LINQ to Object
Standard query operatorsSelectWhereSelectMany...
Extended query operatorsZipAggregate...
LINQ to Observable
Standard query operatorsSelectWhereSelectMany...
Extended query operatorsZipThrottle...
How to move a ball by keyboard (ASDW)?
Imperative Impl.void OnKeyPress(object sender, KeyPressEventArgs e){ if (isPlaying) { if (e.KeyChar == 'a' && ball.Left > 0) { ball.Left -‐= 5; } else if (e.KeyChar == 'w' && ball.Top > 0) { ball.Top -‐= 5; } else ... } else ...}
Declarative Impl.// filter the KeyPress events when playingvar keyPress = GetKeyPress().Where(_ => isPlaying);
// filter the events to move leftvar moveLeft = from ev in keyPress where ev.EventArgs.KeyChar == 'a' where ball.Left > 0 select ev;moveLeft.Subscribe(_ => ball.Left -‐= 5);
// filter the events to move topvar moveTop = from ev in keyPress where ev.EventArgs.KeyChar == 'w' where ball.Top > 0 select ev;moveTop.Subscribe(_ => ball.Top -‐= 5);
Mouse drag and draw
var mouseMove = GetMouseMove();var mouseDiff = mouseMove.Zip(mouseMove.Skip(1), (prev, curr) => new { PrevPos = new Point( prev.EventArgs.X, prev.EventArgs.Y), CurrPos = new Point( curr.EventArgs.X, curr.EventArgs.Y) });
var mouseDrag = from _ in GetMouseDown() from diff in mouseDiff.TakeUntil(GetMouseUp()) select diff;
mouseDrag.Subscribe(d => DrawLine(d.PrevPos, d.CurrPos));
Everything in Web is Asynchronous
User actions
AJAX requests
Animations
...
Reactive Framework in JavaScript
A full featured port for JavaScriptEasy-to-use conversions from existing DOM, XmlHttpRequest, etcIn a download size of less than 7kb (gzipped)
Bindings for various libraries / frameworksjQueryMooToolsDojo...
Mouse drag and drawin JavaScript
var target = $("#paintPad");var mouseMove = target.toObservable("mousemove");var mouseDiff = mouseMove.Zip(mouseMove.Skip(1), function(prev, curr) { return { prevPos: { x: prev.clientX, y: prev.clientY }, currPos: { x: curr.clientX, y: curr.clientY } }; });
var mouseDown = target.toObservable("mousedown");var mouseUp = target.toObservable("mouseup");var mouseDrag = mouseDown.SelectMany(function() { mouseDiff.TakeUntil(mouseUp);});
mouseDrag.Subscribe( function(d) { drawLine(d.prevPos, d.currPos); });
Wikipedia lookup
var textBox = $("#searchInput");var searcher = textBox .toObservable("keyup") .Throttle(500) .Select(function(_) { return textBox.val(); }) .Select(function(term) { return queryWikipedia(term); }) .Switch();
searcher.Subscribe( function(data) { var results = $("#results"); results.empty(); $.each(data, function(_, value) { results.append($("<li/>").text(value)); }); }, function(error) { $("#error").html(error); });
Time flies like an arrow
var container = $("#container");var mouseMove = container.toObservable("mousemove");
for (var i = 0; i < text.length; i++) { (function(i) {
var ele = $("<span/>").text(text.charAt(i)); ele.css({position: "absolute"}).appendTo(container);
mouseMove.Delay(i * 100).Subscribe(function (ev) { ele.css({ left: ev.clientX + i * 20 + 15 + "px", top: ev.clientY + "px" }); });
})(i);
Benefits of Rx
Easy to composite and coordinate async operations
Easy to express the algorithm (code locality)
Easy to be unit tested
...
Rx & Language Features
Features in C# that Rx usesExtension methodLambda expression & closureType inferenceLINQ query expression
Rx has been implemented in ...C# & VBJavaScriptF#
Port to other Languages
Rx can be easily ported to various languagesScalaRubyPythonmodern languages with basic functional features
Almost impossible to implement Rx in JavaCannot extend a type without breaking codeMissing enough functional features
Resources
Matthew Podwysockihttp://codebetter.com/blogs/matthew.podwysocki/
Reactive Framework on MSDN DevLabshttp://msdn.microsoft.com/en-us/devlabs/ee794896.aspx
Tomáš Petříčekhttp://tomasp.net/
Q & A