BEN: Hey, everybody. I will start out a question, with a question
here: are you new to RxJS? When you're using it? Do you feel lost? Helpless? Alone? I'm Ben Lesh. I'm
here to help. I'm excited about this. Actually, a lot of people know me from the
web, and they might not believe I'm Ben Lesh, so
it's me! It's really me. I brought the hat. I don't always wear the hat. I do at conferences, otherwise people don't
know who I am! So how long have you been using RxJS? Who, here, has been using it for longer
than a month? Keep your hands up. Longer than six months? Longer than a year? Oh. It's going down! Longer than two years! Is there even one? Where are you! Stand up, all right. That's awesome. How about longer than five years? That's not even
me, right? I've been using it for a little more than
two years. More or less. That
surprises some people, I suppose, but that's true. But RxJS is more than nine years old. It was created by a developer a long time
as as part of Microsoft Project Volta was targeted compiling C# to JavaScript and wanted
to. Not long ago, I was pretty new to
this. Questions I was asked when I was new to this,
the first question I asked what is this? Another low dash. It looked like low dash to me at first blush. When I did get into
it, I started asking this question all the time: what operator do you I use for that? How do I do this the Rx way, right? Why did that observable die? I put a catch in
there. It shouldn't be dead now. It should just keep going, right? What is this Rx code
I wrote four months ago? These are questions I asked. I'm going to answer a few of
the of these, that is what this talk is about. Usually, when I go to conference, I have to
explain what an observable is to folks. Here, the Angular people have been really
great about promoting reactive programming, including
it in Angular 2. If you're sitting here
and you still don't know what observables are, I will try to explain it but there are
plenty of other resources out there, and even around this conference to learn exactly
what an observable is. We are talking about thinking reactively. You know what an
observable is, and you want to start using them. It's hard to kind of wrap your head
around it when you're used to imperative programming. You need to start viewing your
events in terms of dependencies. So you look at something, for example, like
a drag and drop. A drag and drop is if you are going to write
out what it does, for each mouse down do some target, you start listening to
mouse movements on the documents until a single mouse up on the document. When you look through what you're doing here,
you can identify three different sets of events that we have to compose together. These
are our kind of dependencies to our one sort of event that we are trying to build - we
are trying to build a single drag-and-drop event. I'm going to start off, and we are
going to select a DOM element. This isn't Angular-specific. We will select a DOM
element and create observables from a mouse-down on our target. We will create one
mouse movement from our document, and one from mouse ups on our document. These don't do anything until you subscribing
to them, they're observables. Basically
setting up functions here. We go back and we look, and we say, "What
are we doing inside of this?" In this highlighted section here, we say we
want to compose mouse movements on the document until a single mouse
up. I can do that really easy with Rx,
so that's just mouse moves, take until mouse up. Again, this is just setting up an
observable. It does not do anything until you subscribe
to it. Now we've basically got
this kind of function that is going to set up an event stream of mouse movements until
a mouse up on the document. For each mouse download targeting, you start
listening to that thing we've just made, so, what we
can do, is we can say, okay, for our target mouse downs, I'm going to switch map because
I've got this other asynchronous thing I want to start, and I'm going just to plug
in that previous piece that I made, that mouse movement takeUntil mouse-ups. That's drag-and-drop. That's sort of the
process that you have to think about when you're trying to think your way through
these reactive things. It's kind of backward, but you have to identify
your different streams of events and then look at your problems
to see how to compose them together. So, more thinking reactively. Another way to think about observable is when
you're looking at your imperative code and think how do I convert this into something
observable? Maybe you're trying to write something new,
and you want to write it from scratch, and observables, but you really only
know how to do this imperatively. You can
go into your code base, and you can find any line of code and examine the variables. What you need to know is that given one of
those variables is observable, right? Right
here, we are saying we want to call or do something with C, so, if we had a stream of
C, we could subscribing to it and do something every time it nexts, right? C comes
from adding A and B. If we had streams of A and B, we could combine
them and add them together. With combine latest. If you're just looking at this arbitrary section
of code, one thing to be aware of is there is
probably something that caused either A or B
or both of them to change which is why you need to add them together again and
doSomething again. You need to work backwards and how do I get
a stream of As? You need to think with your goal, work your
way backwards, and recognise where you're getting these streams of information
from in composing them together. So,
here's another one I get with people who start out, which is, "Oh, my God, RxJS is so
confusing." People are more comfortable with promises
or something. They're really,
really stressed out about what operator to use, so confusing. If you find it confusing or
just starting out, stop worrying about the operators. Seriously. Stop worrying with the
operators. Like people get very excited about all these
tools you just handed them, and it's scary to them. You're probably comfortable with promises
before. A promise here on
the top, you've got then and it takes a success function and an error function. Observable, you've got subscribing, a next,
an error, and a complete. It's really not that
much different. One has more characters, I suppose - it's
got another argument, right? But, in all honesty, let's face it, you're
probably only using the first argument in most of
your thens, and the first argument in most of your subscribes, so that's not much
different. If you still think it's different, I will
change the names for you. Quick draw -
both using callback. Now, I don't know which one was which from
before! But
basically, without operators, observables are no scarier than prom sells. You can start
using observables right now and if the operators are freaking you out, subscribing to
them, do imperative programming after that. That's how I started. I have didn't know
all the operators. Maybe you can get your feet wet with map. That's a good starting
point if you don't know what to do - maybe, might be too scary. Which brings us to the
next question that I get, what operator do I use? I want to build all these things, and
it's going to be amazing, and they're going to be so reactive. What operator do I use? People get jarred about that. Remain calm - it is okay! So, the agony, agonising over
what operator we want to deal with. You can use the operator guide where our docs
are. There is a really, really good guided process
that helps you pick what operator you should be using at the bottom of the page. Developed by Andre Staltz - good stuff. You
don't have to Rx everything. People are trying to overuse RxJS. Just take the operator
that you know, and get them up to the point where you can get them, and then
subscribing and do the rest imperatively, if that's what you have to do. It's totally fine. This is so everyone sees, this is a screen
shot, if you do go to reactive.io RxJS, that's where you will find the choose your own adventure
operator finder thing - it's pretty cool. I recommend starting with these operators. I work on a lot of apps at Netflix, and
I help people on the internet, and friends, that use RxJS, and especially when I have
experience porting, you know, Rx 4 and 2 over to RxJS 5. I get a real first-hand look
like at what operators people have and what they're using. These are the common
ones - there are a few more that come up, time, if
people do coincide rate-limiting, things like that. These are the ones that people end up using
a lot. So there are 60-plus
operators, start with these and work your way up from there. You will be a little more
comfortable. This is where I'm going to dive into some
deeper technical stuff about what an observable is doing. I want to try to demystify observables a little
bit. People
look at observables, and they're like, "What is happening inside this? There is this
class, it is doing async things, and it must be unicorns and dark magic in there. There
are schedulers and all this stuff." People get freaked out about what an observable
is really doing. Some of that might be coming from promises,
because promises do have forced async behaviour and nuance where it
is sketching errors and things like that. What if I told you that observable is nothing
but a function? I know I've said this
already a little bit in my talk, but it's really true. To prove it to you, I'm going to show
you what it is like to build an observable that is just a function. If an observable is just
a function, it would be a function in a just took an observer, right? That's all it would
do, it is just a function that accepts an observer - easy-peasy. It's going to call a few
methods on that observer. Here I'm setting up an interval, and I'm nexting
out - technically 11 values in there. Ten values, actually. And then I'm calling complete when
that is done. And the other thing it does is it returns
some teardown logic. In this case,
it is returning a function that clears the interval that I can call at a later point
to tear it down. Whenever I call the function, I call it with
an observer, and the observer is just a plain object with a next air error and complete
method on it. You just pass that in. That is your own subscribe, if you will. So, you get that teardown back, and later,
you can unsubscribe with that. This is where our teardown comes out. We're calling it at a
later point in a set timeout in this case. To move on from that, operators are also just
functions. Operators are function that is take an observable
and return a new observable. In this case, it is functions for returning
the same observable, but it is still a operator. A bit like a noOperator. Anyway, we know that an observable is a function
that takes an observer. We can replace an observable with a function
that takes an observer, and, inside of that what we are
going actually to do is an operator is going to
subscribe to the source, that source observable, with its own observe year, and then it
is going to take the observer it was passed and actually end up calling that from inside
its own observer.s at that taking an other why wrapping it with its own observer so it
can do something. We will make this a map operator. In order to make this a map
operator, all I really did was I renamed it "map" for one and then passed it a mapping
function. I'm calling that mapping function before the
next destination observer, right? So there are two observers inside here. There is the one of that object-literal that
I passing to, and the other one that I'm passing values to, and I'm also calling a map
function. If we use it with our observable we created
earlier, it would look like this. We
are passing our observable to it in a mapping function, and then we are going to go
ahead and subscribe to it, log it out, and it will next out these values. It's pretty cool. We've got one mapping in there. What is it going to look like if we chain
them? All
right, we've got map, and then another called a map with my observable in it. Two
functions in there. What happens? It comes out, with it's actually chaining,
getting the exclamation point and question mark in there
back to back. What happens if we want
to do a lot of operators? Let's see, we've got an observable, we are
scanning it, and then we are filtering it, mapping it a couple
of times. That's pretty gross. It's really not
cool. Maybe we could do code for mat to go make
this better? No, still gross! So
I think it would be nice if we could do dot-chaining which enables us to have this much
easier-to-read format. Let's wrap our observable functions in a class
so we can have dot-chaining. We are going to have this class called observable
and have the constructor accept the observable function
we were making before and we are going to set it as a property on our class, and then
whenever we want to invoke our observable, we just - we actually just call this observable
function on our observable instance, but that's not the best name in the world. We could probably change that to subscribe. Now
we can add operators to the class, and when we add operators to the class, and the
operators are also returning a new instance of the same class, that means whatever
they return has the same operator, and you can chain them together. Right? Now it is
much, much more readable. I didn't actually change that much from the
code this this map operator that's in here, the code for
it is almost identical to what I showed you earlier. I refactored it a little bit so it could be
a member on this class. So, when I run
this, I get my map chain executes, if it's much, much prettier to read. So observable as
a class provides dot-chaining. The ability to add safety to the type, so,
one of the things that observable does inside of itself is that
it makes sure that you have passed. If
you've only passed a next handler, it's going to rough in error handler and completion
handler for you to do some default behaviours. It's also going to kind of wrap your
observer and make sure there are guarantees like you can't call next after it's
completed, and you can't call error after it's completed, and so on. You can't called next
after it's errored, and all these things. It provides some performance optimisations,
and in Rx, we can look at properties in the class
and make decisions about what we want to do with it in certain operators. Scaler values can be processed a little quicker
that way. But really, really, all that it's actually
there to do is wrap that observable function. It's
just a function that takes an observer. Think of observables as functions. Don't think of
them as anything else. They're not doing anything when you've been
handed them. They're not running anything, they're just
functions. So, functions again, don't do
anything until they're called. The observables are lazy. That's another word you hear a
lot, that you have to subscribe to it before it will do anything. Operators take your
observable and return it into an observable. Operators return an observable, and an
observable doesn't do anything. Calling "map" isn't going to execute anything
in your observable. That's a misunderstanding IBA seen a few times
as well. I want you to
notice something else. This is my map operator from before. So, if you look at this map
operator, you see that I'm returning a function, I'm returning an observable, but we can
think of it as a function, and, inside of it, it's creating an observer, what is in
white, and passing valuation to another observer which
is in green. It is chaining these observers
together. It is when you call the function, it's basically
saying, "I'm going to set up a whole bunch of observers that are all talking
to each other." That's an observable chain. Right? So, it's just a function that connects chains
of observers, so you've got a safe observer at the top where you're producing
your data. You've got one for your filth,
yes, one for your map at the bottom, your wrapped observer, so, in this case, it would
take your next handler and wrap it in a server. It is chaining them together just like you
saw before. So let's actually visualise this. I've got this observable interval. In my
observable interval, after that, I'm going to filter it, then go and map it, and subscribe,
log it out, whatever I'm going to do with those values. These rectangles here are
represent observers, and I have one for my producer at the head. The other thing to
note is that each one of those represents a different notification channel. My next
channel, my error channel and my complete channel, those horizontal lines going
across. I have one at the head, one for my filters,
one for my wrap, and then finally one that wraps my handlers. If I'm stepping through this, the producer
next handler zero at the filter observer. The filter observer passes for zero when it
runs that assertion on it. Then it nexts along to map and sent along,
0+0 is 0. Next we're going
to send along 1. It goes to the filter and does not pass. If it doesn't pass, it's the it's
not going to be sent along. That's it. So what about error-handling? Error-handling, this
is probably one of the bigger points of confusion for people, so I'm going to spend a bit
of time on that. A thing to note about observers, they actually
have rules. Observers
will no longer pass along values if error has been called, if complete has been called,
or if you have unsubscribed. At that point, the observer will be closed. There is actually a -
it will no longer allow you to next out things or error. What does that mean for
error-handling? Let's look at this contrived example. We've got an interval again. In my
interval, I'm going to throw an error whenever I hit the value 1. So, first it is end zero
and that goes along. We are map it, and we're not doing anything
with it which makes it to our subscription. The next time we sent along one, and 1 throws
in our map. So
the observer right then and there, that particular observer is marked as closed because
error has been called on it. Nothing can pass through it at that point. You can't next on
it, can't error, you can't complete, which means that everything upstream from it is
gone. It can't send anything through any more. And then the error is signalled down
the chain, and after that, you're done. So, to handle an error, we have the catch
operator, and, just to explain how this works, it takes a function that gives you the
error and you're expected to return an observable, that it's going to use in place of the
observable that just failed. So it's very similar to promise-catch in its
structure, but what it is doing under the hood is a little different
because we are doing an observable, so, using catch on the same sort of example, this
time I've got an observable of just the value 1, and map is going to throw because
it hates 1, but I don't know who writes code like this - but I do! We're going to catch that and return the observable
2. What
happens here is we send the 1. Map fails because it hates 1s, for whatever
reason, and we send the error to the error-handler on
catch. Well, the catch observer just had its
error-handler called so it is now closed. What it's going to do is it's going to say,
"Hey, let's actually map this to this new observable
of 2." And so it creates a new observer to
subscribe to that observable at 2. We set up our error channels again, and then
we signal along 22 to the next handler, and then,
after that, because it's an of, it's going to
complete right afterwards which means that one is now closed and done, and we're all
done. So what is there is catch is saying, "Yeah,
this died up here, but I will provide this other observable for you to finish out your
process." But catch, you know, it is still let
our whole observable die. It's upstream from it. It's now gone. When you run into
certain situation, you don't really want that. How do you solve that problem? How do
you keep your observable from dying when you catch an error? Again, this is a very
common question. You isolate your observer chains. So we now are setting up these
chains of observers whenever we are dealing with observables. So let's talk about the
interval you don't want to die. In this case, we are doing something where
we are polling. Every ten seconds, we will make an Ajax call,
and, if it fails, which can very well could, we don't want it to die. This is what people try at the beginning,
and this going to work and I will show you why. At first, we send along zero and we make our
Ajax request which starts off a brand new observable. It's going to wire things up to signal
back into our switch-map observer, and what is going to happen is when it merges the
value back in, that's going to be passed through because we will say this first Ajax
request was successful, and it's going to go all the way through, it gets passed through
catch because catch doesn't care about next handling, right to our next handler. The
next time we are going to send one, and this time, I'm going to say this this Ajax
observable is going to die, so this observable, we set it up, we subscribed to it, and it's
got errors. If it has errors, then both of these have
had their error-handlers called, they're closed. You can't send anything through them any more. That means that
everything upstream from that, including the interval, is now dead. Well, at the very
least, can't send anything through. So what do we do? The catch observer is
like, "Okay, it's the it's closed, and we will replace that with an empty because that's
what it mapped to." Empty completes right away, it doesn't have
any value. And the
whole thing is done. Oops. That's not good. How do you keep an interval alive? Countless - countless emails! [LAUGHTER]. Let's solve this. What if we put the catch
inside of the switch map this? Let's see what happens. I'm going to jump right to the
error scenario here. The interval fires; the switch map creates
the Ajax observable; and this time with a catch: so now we've got this
observable chain sitting off to the side, and the Ajax is going to error, and that error
is going to go to our catch. Our catch is
going to map to a new observable of empty, and empty completes, but switch map has
this behaviour where it says, "Hey, my source isn't complete yet, so I don't really care
if this child observable is complete, I'm going
to keep going." While that one dies, the
external, the main observable chain, the main observer chain, stays alive. So, when the
interval fires again, switch map fires, create another Ajax - you get the idea. So what
happened? We created a second observer chain. Whenever you have an error, your
observer chain dies, so if you can isolate your observer chain, you won't have a
problem, particularly if you punctuate that second observer chain with a catch or a
retry, or something that prevents that error from being pushed back down into the
main observer, or the main observer chain. So that is going to shield our main observer
chain. That's what we want. The tl;dr of this is if you're having a - merge
maps, switch map, what have you, unless it's okay for your
observable to die, in which ways, handle it where you choose. Another thing that I see when I'm helping
people is they will end up with some memory leaks or something like
that: subscription management mistakes. This is very common, and that's usually comes
from failing to unsubscribe. So
failing to unsubscribe will cause memory leaks, resource leaks of various sorts, because
unsubscription is what tears down the resources you set up when you subscribe to
begin with, so think about like add event listener, and remove event listener. I think
many of us have set up an event listener and forgotten to use it and don't know why it
is using a whole gig on Chrome - that's amazing. So this is one way to manage your
subscription. Inevitably, you will have to do this with
Rx, managing imperatively. You
will have to create a subscription and later on unsubscribe. People end up wanting to
do this everywhere, and they end up with something like this. Now, when you look at
this, I mean, who here can spot the one that I missed? Yeah, four was missing. It's
really easy when I line them up like that. But what is it like when all that stuff is
mixed into all the rest of your code. Right, when it is sprinkled throughout this
complex application, it becomes very, very easy to
miss something and cause leaks. So there is
another way to do this: you can manage mostly declaratively. You still have to have a
subscription in there somewhere. In order to manage declaratively, you can
do things like "take until". I've got these kill observables coming up
from up here, coming interest from a stream of route changes, one like that,
one is a subject, and that basically means that you can kind of take all your observables,
merge them into one, so you only have one subscription and use takeUntil to
take them until you want to. You could add
a do block to handle the subscribe logic individual for each one of those streams if you
wanted if you didn't want to handle it in your subscribe later on. But there are a lot of
ways to handle this declarative subscription managing - they all behave similarly. There
is switch and switch map which have an inherent unsubscription behaviour built into
them. So, the good sides of declarative subscription
management is you have fewer subscriptions to manage so you're less likely
to miss one of those and forget to unsubscribe. The other interesting thing is you can use
any event stream to trigger an unsubscribe. If you have a stream like this is to kill
my Ajax call, whatever, you can do whatever you want to make sure that still
happens. And, again, you can compose this
any number of ways. I mean anything that can be an observable,
you can use to kill your other observables. So there is another approach to a subscription
management, though, let your framework or libraries handle
it for you. A lot of libraries and
frameworks have a handling for RxJS observables built in. You can see the pipe async
where you're right out this observable. So just some studio code example here. If you
have a template here, you can write out foo$ with the pipe async, as you have the
observer interval, as it updates, it will cause your view to update. There is a gotcha to
this that people don't realise. The gotcha is that, right here, I'm going
to log out when the timer's fired. The timer, like everybody knows, is a set
timeout. It will fire one time
and give us a value and that's it. Most people would expect only to see timer
fired logged out one time. When I run this, I see it fired twice. And this is people
like, "I don't know why this is happening twice." Let's say it is an Ajax call or something
underneath that, you've made the Ajax call twice, people get bent out of shape like that
and don't like that. Why is that? Because you've created two subscriptions,
and, as I've stated for most of this talk, observables
are just functions. They don't have any state or
anything, they're just kind of, "I'm going to go ahead and I'm going to set up this whole
observer chain every time you subscribe to me all the way up to the top." So it does
that twice up the top, it sets up the timer twice in this case, and the timer fires twice. So, here's how we fix that. We fix that by adding a share to the end of
our observable chain. What that is going to do is that is going
to multicast. It basically means that it's
going to wait for the first thing to subscribe to it, that will set up the observer, the
observer chain, and then the next guy that subscribes to it, that's going to get wired
into that same observer chain. When that observer chain fires, it is going
to broadcast to everybody that's listening. But fair warning: multicasting comes with
costs. So the
costs around multicasting is you're going to end up allocating a subject, you're going
to end up allocating an array to stuff all your
servers in, you will end up allocating a couple more subscriptions you don't want to multicast
all over the place. If you find yourself
doing share, share, share, share, take a step back and examine what you're doing and
why. Pipe A sync has pros which is no subscription
management at all, which is really good. The other thing that is really cool is you've
got this automatic unsubscription when something is removed from your view. Let's say like you have a web socket or
something feeding into some data and you had it set up so your observable closed the
web socket when you unsubscribed, that means as soon as you - if something out of
your view, the web socket will automatically close. So it is really a cool way to consume
observables. The con to this is that it can encourage the
overuse of multicasting, so people using share all over the place, and
that can impact performance. But a good
subscription rule-of-thumb that I use is more than two is probably too many. If I find
myself managing three, four, five observables, and single component or a single
module, then managing that many subscriptions rather, in a single module, I think to
myself, this is probably gone overboard on this, should use a more declarative
approach. All right, so this is the final point that
I'm going to make here, and the very first talk I ever did for the Angular community
was at ngConf a couple of years ago. I contradict myself, don't Rx all the things! You can build your app as one big
observable, but please don't. I've seen this. I have a lot of friends in the Rx community,
and I have had some really brilliant people come up to me, "Ben, check this out! I made a whole code and it's one observable. You can subscribe to it, and it does
everything." I'm like, "Really, I would like to see that. There are probably five people in
the world that can read this!" [LAUGHTER]. Rx is a domain-specific language. It takes a
while to learn it. Once you learn it, it is extremely powerful,
it makes your code we readable, but you should Rx where it's best
suited. So, it's best suited for doing things
like multiple events together, like drag-and-drop. I've done other things where I have
had to do brush selection on a graph where you click and you select like a little range
or whatever. Be things like that, it works really, really
well for because it is very easy to modify those sorts of things and alter it
declaratively. It's good for adding delay, or
client-side rate limiting, maybe setting a timeout, things that involve adding times
to your events. It's really good for co-ordinating async tasks. If you have a bunch of
things happening asynchronously and you want to make sure when they come in, they
come in together, or that your you're co-ordinating various things, maybe web sockets
and workers, whatever, it is really, really good for that. It's also great when cancellation
is required. If you have Ajax requests you want to be able
to cancel or abort like I talked about, most of my other talks, it's,
they're fantastic for that. However, do not
use Rx when it is not needed. If you've got simple button clicks, you don't
need an observable to add an event listener to a button. That gets silly. You don't need an
observable to wrap everything in your form and update something so that you can
submit it later. "Hello, World" apps do not need Rx, right? I can tell you for a fact, and
I'm sad to say I've actually seen that. Thinking reactively, just to recap: when you
think reactively, you kind of want to work backwards. You think about your goal and
you think about what all of your event streams that you're going to read that goal, to
build together like Lego pieces to get to that goal. Any variable can be an observable. Anything that you look at, just, again, imagine
that code executes once in a while over time, and that variable changes. That's a value that changes over time. It can be
represented as an observable. Observables are just functions. There's no deep magic
inside of them. Don't be scared to use functions every day. You will be fine. But
observables get too much credited. Observer chains do all the work. An observable sets
up this chain of observers and then it goes away. It's just a function you called and it's
done. There are templates to set up these observer
chains. Everybody talks about
observables. Really, it is the observers that matter. Calling error actually will break that
chain. So that is an important thing, observers have
rules. If you error, complete,
unsubscribe, that observer in your chain is done. And, just remember your subscription
management rules. Two or three or more is probably too many
subscriptions in a single component in my opinion. If you like that sort of thing, go nuts, make
sure to have tests around that, though. Then only use Rx where it makes sense. I love Rx. I think it
is really cool when people show me stuff, like that one big observable, it's so much
fun to get that this stuff. That's why I'm in it. You know, don't make your team hate you! Don't go out and be like, "Oh, our whole app
is in here now, it is great!" Your team will
not like you, even if they think this person really knows their Rx, I would recommend
against that. That's it. Thank you very much. [APPLAUSE].