Writing a truly non-blocking, maintainable code shouldn’t be a giant headache. As developers we strive to write features bundled up into a single, readable and testable block of code and today’s technologies greatly help us. From .NET events to Task Parallel Library, we can craft complex features. But, unfortunately, having that many different technologies, we lack the possibility of composing (easily) between them. In this session, you will learn about the core fundamentals which construct the Rx API and how it provides a single, uniform language that abstracts away all sources of events.
4. Agenda
History
Build the Rx API – Observables inside-out
• All about the callbacks
• First building blocks
• Different Rx operators
Live demos
• Google search
• Drag and drop
“Cold” vs “Hot”
Summary & references
4
@stas_rivkin
5. About Me
More than 6 years of hands on with various technologies
• .NET stack (desktop mainly)
• Angular
• NodeJS
https://blog.oz-code.com/author/stasr
5
@stas_rivkin
Senior software consultant
6. interface IEnumerable<out T>
{
IEnumerator<T> GetEnumerator();
}
interface IObserver<in T>
{
void OnNext(T value);
void OnError(Exception error);
void OnCompleted();
}
interface IEnumerable<out T>
{
IEnumerator<T> GetEnumerator();
}
Eric Meijer
Push & Pull models
https://goo.gl/2wzXbe
PRO Tip – 36m:00
2009 – Rx released
Rx History
6
@stas_rivkin
interface IObservable<out T>
{
IDisposable Subscribe(IObserver<T> obs);
}
interface IEnumerator<out T>: IDisposable
{
bool MoveNext();
T Current { get; }
}
interface IEnumerator<out T>: IDisposable
{
bool MoveNext();
T Current { get; }
}
7. static void Main(string[] args)
{
Console.WriteLine("Before");
var list = new List<int>(new[] { 10, 20, 30, 40, 50, 60 });
list.ForEach(num => Console.WriteLine(num));
Console.WriteLine("After");
}
All About the Callbacks
7
@stas_rivkin
8. public class Program
{
static void Main(string[] args)
{
Button myBtn = new Button();
myBtn.Click += MyBtnOnClick;
}
private static void MyBtnOnClick(object sender, EventArgs eventArgs)
{
Console.WriteLine("Clicked");
}
}
public class Button
{
public event EventHandler Click;
public void RaiseClickEvent() => Click?.Invoke(this, EventArgs.Empty);
}
All About the Callbacks
8
@stas_rivkin
9. static async Task Main(string[] args) => await RunAsync(OnNext);
private static async Task RunAsync(
Action<string> onNext)
{
WebClient client = new WebClient();
onNext(await client.DownloadStringTaskAsync(“some-URL”));
}
private static void OnNext(string value) => Console.WriteLine(value);
All About the Callbacks
9
@stas_rivkin
21. First Building Blocks
21
static void Main(string[] args)
{
var observer = new ConsoleObserver();
NeedData(observer);
}
public static void NeedData(ConsoleObserver observer)
{
foreach (var value in new[] { 10, 20, 30 })
{
observer.OnNext(value);
}
observer.OnCompleted();
}
public class ConsoleObserver
{
public void OnNext(int value)
{ ... }
public void OnError(Exception error)
{ ... }
public void OnCompleted()
{ ... }
}
public interface IObserver<in T>
{
void OnNext(T value);
void OnError(Exception error);
void OnCompleted();
}
@stas_rivkin
22. First Building Blocks
22
public interface IObserver<in T>
{
void OnNext(T value);
void OnError(Exception error);
void OnCompleted();
}
static void Main(string[] args)
{
var observer = new ConsoleObserver();
NeedData(observer);
}
public static void NeedData(IObserver<int> observer)
{
foreach (var value in new[] { 10, 20, 30 })
{
observer.OnNext(value);
}
observer.OnCompleted();
}
@stas_rivkin
23. First Building Blocks
23
public interface IObserver<in T>
{
void OnNext(T value);
void OnError(Exception error);
void OnCompleted();
}
static void Main(string[] args)
{
var observer = new ConsoleObserver();
Subscribe(observer);
}
public static void Subscribe(IObserver<int> observer)
{
foreach (var value in new[] { 10, 20, 30 })
{
observer.OnNext(value);
}
observer.OnCompleted();
}
@stas_rivkin
24. public class Observable : IObservable<int>
{
public void Subscribe(IObserver<int> observer)
{
foreach (var value in new[] { 10, 20, 30 })
{
observer.OnNext(value);
}
observer.OnCompleted();
}
}
public interface IObservable<out T>
{
void Subscribe(IObserver<T> observer);
}
First Building Blocks
24
static void Main(string[] args)
{
var observer = new ConsoleObserver();
var observable = new Observable();
observable.Subscribe(observer);
}
@stas_rivkin
25. First Building Blocks
25
public class Observable : IObservable<int>
{
public IDisposable Subscribe(IObserver<int> observer)
{
foreach (var value in new[] { 10, 20, 30 })
{
observer.OnNext(value);
}
observer.OnCompleted();
return Disposable.Empty;
}
}
public interface IObservable<out T>
{
IDisposable Subscribe(IObserver<T> observer);
}
@stas_rivkin
static void Main(string[] args)
{
var observer = new ConsoleObserver();
var observable = new Observable();
observable.Subscribe(observer);
}
26. First Building Blocks
26
static void Main(string[] args)
{
var observer = new ConsoleObserver();
var observable = new Observable(new[]{ 10, 20, 30 });
observable.Subscribe(observer);
}
public class Observable : IObservable<int>
{
private IEumerable<int> _values;
public Observable(IEumerable<int> values)
=> _values = values;
public IDisposable Subscribe(IObserver<int> observer)
{
foreach (var value in _values)
{
observer.OnNext(value);
}
observer.OnCompleted();
return Disposable.Empty;
}
}
@stas_rivkin
27. First Building Blocks
27
public class Observable<T> : IObservable<T>
{
private IEumerable<T> _values;
public Observable(IEumerable<T> values)
=> _values = values;
public IDisposable Subscribe(IObserver<T> observer)
{
foreach (var value in _values)
{
observer.OnNext(value);
}
observer.OnCompleted();
return Disposable.Empty;
}
}
@stas_rivkin
static void Main(string[] args)
{
var observer = new ConsoleObserver();
var observable = new Observable(new[]{ 10, 20, 30 });
observable.Subscribe(observer);
}
28. First Building Blocks
28
public class ToObservable<T> : IObservable<T>
{
private IEnumerable<T> _values;
public ToObservable(IEnumerable<T> values)
=> _values = values;
public IDisposable Subscribe(IObserver<T> observer)
{
foreach (var value in _values)
{
observer.OnNext(value);
}
observer.OnCompleted();
return Disposable.Empty;
}
}
static void Main(string[] args)
{
var observer = new ConsoleObserver();
var observable = new ToObservable(new[]{ 10, 20, 30 });
observable.Subscribe(observer);
}
@stas_rivkin
29. public static class ObservableExtensions
{
public static IObservable<T> ToObservable<T>
this IEnumerable<T> src)
=> new ToObservable<T>(src);
}
static void Main(string[] args)
{
var observer = new ConsoleObserver();
IObservable observable = new[]{ 10, 20, 30 }
.ToObservable();
observable.Subscribe(observer);
}
IEnumerable -> IObservable
29
@stas_rivkin
30. static void Main(string[] args)
{
var observer = new ConsoleObserver();
IObservable observable = new[]{ 10, 20, 30 }
.ToObservable();
observable.Subscribe(observer);
}
Where
30
@stas_rivkin
public static class ObservableExtensions
{
public static IObservable<T> ToObservable<T>
this IEnumerable<T> src)
=> new ToObservable<T>(src);
public static IObservable<T> Where<T>(
this IObservable<T> src,
Func<T, bool> filter)
=> new Where<T>(src, filter);
}
31. Where
31
@stas_rivkin
public static class ObservableExtensions
{
public static IObservable<T> ToObservable<T>
this IEnumerable<T> src)
=> new ToObservable<T>(src);
public static IObservable<T> Where<T>(
this IObservable<T> src,
Func<T, bool> filter)
=> new Where<T>(src, filter);
}
static void Main(string[] args)
{
var observer = new ConsoleObserver();
IObservable observable = new[]{ 10, 20, 30 }
.ToObservable()
.Where(n => n > 10);
observable.Subscribe(observer);
}
32. Where
32
public class Where<T> : IObservable<T>
{
private IObservable<T> _src;
private Func<T, bool> _filter;
public Where(
IObservable<T> src,
Func<T, bool> filter)
{
_src = src;
_filter = filter;
}
public IDisposable Subscribe(IObserver<T> observer)
{
//...
}
}
@stas_rivkin
33. Where
33
public class Where<T> : IObservable<T>,
{
private IObservable<T> _src;
private Func<T, bool> _filter;
private IObserver<T> _observer;
public Where(
IObservable<T> src,
Func<T, bool> filter)
{
_src = src;
_filter = filter;
}
public IDisposable Subscribe(IObserver<T> observer)
{
_observer = observer;
return new CompositeDisposable
{
_src.Subscribe(this),
Disposable.Create(() => _observer = null)
};
}
/* */
}
@stas_rivkin
IObserver<T>
34. Where
34
public class Where<T> : IObservable<T>, IObserver<T>
{
private IObservable<T> _src;
private Func<T, bool> _filter;
private IObserver<T> _observer;
public Where(
IObservable<T> src,
Func<T, bool> filter)
{
_src = src;
_filter = filter;
}
public IDisposable Subscribe(IObserver<T> observer)
{ ... }
public void OnNext(T value)
{
if(_filter(value)) _observer.OnNext(value);
}
public void OnError(Exception error) => _observer.OnError(error);
public void OnCompleted() => _observer.OnCompleted();
}
@stas_rivkin
35. static void Main(string[] args)
{
var observer = new ConsoleObserver();
IObservable observable = new[]{ 10, 20, 30 }
.ToObservable()
.Where(n => n > 10);
observable.Subscribe(observer);
}
Select
35
public static class ObservableExtensions
{
public static IObservable<T> ToObservable<T>
this IEnumerable<T> src)
=> new ToObservable<T>(src);
public static IObservable<T> Where<T>(
this IObservable<T> src,
Func<T,bool> filter)
=> new Where<T>(src, filter);
public static IObservable<TOut> Select<TIn, TOut>(
this IObservable<TIn> src,
Func<TIn, TOut> selector)
=> new Select<TIn, TOut>(src, selector);
}
@stas_rivkin
36. Select
36
static void Main(string[] args)
{
var observer = new ConsoleObserver();
IObservable observable = new[]{ 10, 20, 30 }
.ToObservable()
.Where(n => n > 10)
.Select(n => n * 10);
observable.Subscribe(observer);
}
@stas_rivkin
public static class ObservableExtensions
{
public static IObservable<T> ToObservable<T>
this IEnumerable<T> src)
=> new ToObservable<T>(src);
public static IObservable<T> Where<T>(
this IObservable<T> src,
Func<T,bool> filter)
=> new Where<T>(src, filter);
public static IObservable<TOut> Select<TIn, TOut>(
this IObservable<TIn> src,
Func<TIn, TOut> selector)
=> new Select<TIn, TOut>(src, selector);
}
37. public class Select<TIn, TOut> : IObservable<TOut>, IObserver<TIn>
{
private IObservable<TIn> _src;
private Func<TIn, TOut> _selector;
private IObserver<TOut> _observer;
public Select(
IObservable<TIn> src,
Func<TIn, TOut> selector)
{
_src = src;
_selector = selector;
}
public IDisposable Subscribe(IObserver<TOut> observer)
{ ... }
public void OnNext(TIn value)
{
_observer.OnNext(_selector(value));
}
public void OnError(Exception error) => _observer.OnError(error);
public void OnCompleted() => _observer.OnCompleted();
}
Select
37
@stas_rivkin
38.
In Action
38
static void Main(string[] args)
{
var observer = new ConsoleObserver();
IDisposable subscription = new[]{ 10, 20, 30}
.ToObservable()
.Where(n => n > 10)
.Select(n => n * 10);
.Subscribe(observer);
}
Where(n => n > 10)
Select(n => n * 10)
@stas_rivkin
39. Filtering Projection Partitioning Joins Grouping Set
Element Generation Quantifiers Aggregation Error HandlingTime and
Concurrency
Where
OfType
Select
SelectMany
Materialize
Skip
Take
TakeUntil
CombineLatest
Concat
join
GroupBy
GroupByUntil
Buffer
Distinct
DistinctUntilChanged
Timeout
TimeInterval
ElementAt
First
Single
Range
Repeat
Defer
All
Any
Contains
Sum
Average
Scan
Catch
OnErrorResumeNext
Using
Rx Operators
@stas_rivkin
Timestamp
47. var dragObs = mouseMoveObs.SkipUntil(mouseDownObs).TakeUntil(mouseUpObs)
Drag and Drop
47
var mouseDownObs = Observable.FromEventPattern(UIElement, nameof(UIElement.MouseDown));
var mouseMoveObs = Observable.FromEventPattern(UIElement, nameof(UIElement.MouseMove));
var mouseUpObs = Observable.FromEventPattern(UIElement, nameof(UIElement.MouseUp));
.Repeat();
On each mouse-down on your target,
start processing mouse-moves for your target
until a single mouse-up on your target
On each mouse-down on your target,
start processing mouse-moves for your target
until a single mouse-up on your target
;
@stas_rivkin
54. “Cold” vs “Hot”
54
Observables are “cold” by default
•“Cold” creates a new producer each time a consumer subscribes to it
•“Hot” observables share a single producer with every consumer that
subscribes to them
var aStream = Observable.Where(...).Select(...)
aStream.Subscribe(...); var shared = aStream.Publish().RefCount();
aStream.Subscribe(...);
aStream.Subscribe(...);
shared.Subscribe(...);
shared.Subscribe(...);
shared.Subscribe(...);
@stas_rivkin
55. Summary
Simple idea – definitely not some magic
Easy to compose between events, callbacks, time-based operations
• Treating events as collections
• Manipulating sets of events with “operators”
Observables are LAZY by nature
• “subscribe” to the Observable sometime in the future
• “dispose” from the observable to cancel & tear down the producer
55
@stas_rivkin
Don’t get me wrong! I love async-await… I think it made our async lives much more easier!
But the problem with async-await is, it can’t be concise and the reason it can’t be concise is because await doesn’t help me get rid of state, immutable state.
The enemy of any user-interface application.
The more mutable state I have, the harder it makes me to figure out the state of my program.
The other thing is, I want describe an idea, a feature - in a one readable place without going through some sort of a callback chain.
Sooo thing that I’m excited about with Rx, is it allows you to write that kind of code, a lot more dependable, a lot more relible… async code
I can write something.. a I’m pretty sure it is going to do what I what…
Ohh… and it’s testable… I can write tests which run the same way every time…
My name is Stas Rivkin
**and you already figured out from my name, I do speak Russian. But this talk will be in English, cause frankly I have no clue how to pronounce software technicalities in Russian.
I’m a software consultant @codevalue Israel.
CodeValue is a consulating company and we are also the proud development center of OzCode the amazing debugging extensions for visual studio. Few of it’s key features is visualizing LINQ debugging and filtering collection in run-time.
I have more than 5 years of hands on experience in .NET C#, mainly in client-side development. I’ve build applications from physics analytics to C&C geo-location based applications.
I’m also a OzCode Evangelist and a blogger. You can check-out few blogs I’ve wrote on “How to mongoDB in C#”
And that’s the end of my self promotion(it never hurts right?).
So.. Let’s talk about reactive extensions… how many of you guys heard about reactive extensions?
So want do we really want?
We want to have one language which talks about all those three things..
Cause really events and callbacks… and a task returning from being completed are really all events, right? They all do the same thing. But we can’t refer to them at the same language.
We need a func of something for the callback and a delegate of some sort for event, and we need to await something to be completed.
I’m treating all of those unified things as a separate things and I can’t combine them.
And when I can’t combine them easily.. I can’t describe how they relate.. Easily.
???So if I have a UI button that has a click event and I can’t describe how that click event is related to this async go and do google search??
Sooo… what if we try to make the most generic API which handles all of those scenarios in a unified language ?
So…
We have an Observer object which wants to observe something..
So this makes that object to an Observable… TA-Daaa..
So…
We have an Observer object which wants to observe something..
So this makes that object to an Observable… TA-Daaa..
So…
We have an Observer object which wants to observe something..
So this makes that object to an Observable… TA-Daaa..
So…
We have an Observer object which wants to observe something..
So this makes that object to an Observable… TA-Daaa..
So…
We have an Observer object which wants to observe something..
So this makes that object to an Observable… TA-Daaa..
So…
We have an Observer object which wants to observe something..
So this makes that object to an Observable… TA-Daaa..
So…
We have an Observer object which wants to observe something..
So this makes that object to an Observable… TA-Daaa..
So…
We have an Observer object which wants to observe something..
So this makes that object to an Observable… TA-Daaa..