RxJS Top Ten - Code This, Not That
Summary
TLDRThis video script offers an insightful introduction to RxJS, a JavaScript library for managing asynchronous streams of data. It explains the importance of RxJS in handling data over time, such as DOM events and WebSockets, and how it overcomes 'callback hell'. The tutorial covers the installation, basic concepts like observables, subjects, and operators, and demonstrates practical examples. It also addresses common issues like memory leaks and strategies to prevent them, making it an informative guide for developers looking to harness the power of reactive programming.
Takeaways
- 🎵 RxJS is a challenging but rewarding JavaScript library for managing data flow over time.
- 📈 RxJS is popular because it handles asynchronous data streams more efficiently than JavaScript alone.
- 🔄 The core concept in RxJS is the 'Observable,' which acts as a data wrapper that can be subscribed to.
- 🚰 Think of using RxJS like plumbing, with Observables as pipes for data.
- 🌳 RxJS is tree-shakable, meaning you should only import the classes and operators you need to minimize bundle size.
- 🔔 An Observable notifies subscribers whenever data changes, making it a key part of reactive programming.
- 🔄 Subjects in RxJS are hot Observables that allow new values to be pushed to them, like a pump.
- 🌟 Behavior Subjects store the last emitted value, ensuring all subscribers receive the latest data.
- ⚙️ Operators in RxJS help manipulate data flow; examples include 'map,' 'filter,' 'scan,' 'tap,' 'debounce,' and 'throttle.'
- 🛠️ RxJS offers tools for error handling (e.g., 'catchError') and memory leak prevention (e.g., 'takeWhile,' 'takeUntil').
Q & A
What is RxJS and why is it considered difficult but rewarding to learn?
-RxJS is a JavaScript library for reactive programming using Observables to handle asynchronous streams of data. It's considered difficult due to its complex concepts and functional programming approach, but rewarding because it provides powerful tools for controlling data flow over time.
How does RxJS compare in popularity to Angular and other libraries?
-RxJS is quite popular, with more daily npm downloads than Angular. It is one of the largest functional libraries, second only to lodash, indicating its widespread use in handling asynchronous data streams.
Why do we need RxJS when we already have Promises in JavaScript?
-While Promises are useful for handling single asynchronous values, RxJS is needed for dealing with real-time streams of data. Promises do not support multiple values over time, which is where RxJS, with its Observables, comes into play.
What is the fundamental class in RxJS and how is it used?
-The fundamental class in RxJS is the Observable. It acts as a wrapper for data that can be subscribed to, notifying subscribers of new data changes over time.
How can one prevent bundling the entire RxJS library in their project?
-To prevent bundling the entire library, one should import only the specific classes and operators needed in their source code. This practice, known as tree shaking, significantly reduces the final bundle size.
What is the difference between the 'of' and 'from' functions in RxJS?
-The 'of' function creates an Observable from a set of arguments and emits each of them as individual items. The 'from' function takes an array, promise, or iterable and emits each individual item from the source.
How can RxJS be used to handle DOM events?
-RxJS can create an Observable from DOM events using the 'fromEvent' function. By passing a DOM element and the event type, an Observable can be set up to emit events each time the specified event occurs on the element.
What are the differences between hot and cold observables in RxJS?
-Hot observables can have multiple subscriptions and can emit items regardless of the number of subscribers. Cold observables, on the other hand, create a new execution for each subscription and do not emit items until a subscription is made.
What is a Subject in RxJS and how does it differ from an Observable?
-A Subject in RxJS is a special type of Observable that allows values to be multicasted to many Observers. Additionally, it has an added 'next' method to push new values into the stream after it has been created, unlike a regular Observable.
What are some common operators used in RxJS to control the flow of data?
-Some common operators in RxJS include 'map' for transforming emitted values, 'filter' to emit only certain values, 'take' to emit only a specific number of values, and 'catchError' to handle errors within the Observable stream.
How can one prevent memory leaks when using RxJS subscriptions?
-Memory leaks can be prevented by unsubscribing from subscriptions when they are no longer needed. Alternatively, using operators like 'takeWhile' can automatically stop the subscription when a certain condition is met, thus avoiding the need for manual unsubscription.
What is the purpose of the 'switchMap' operator in RxJS?
-The 'switchMap' operator is used to switch from an outer Observable to an inner Observable based on the emitted values of the outer Observable. It is particularly useful for handling relational data or when you need to switch context based on the data flow.
What are 'combineLatest' and 'merge' operators in RxJS and how do they differ?
-The 'combineLatest' operator takes an array of Observables and emits an array containing the latest values from each Observable whenever any of them emit a new value. The 'merge' operator, on the other hand, subscribes to each Observable in the array and simply emits all values from them as they come in, without waiting for the others.
Outlines
🌐 Introduction to RxJS
This paragraph introduces RxJS as a challenging yet rewarding JavaScript library that manages data flow over time. It highlights the library's popularity, surpassing daily npm downloads of Angular, and emphasizes its utility in handling asynchronous streams of data, such as those from Firebase, DOM events, WebSockets, and file uploads. The speaker explains that JavaScript lacks native tools for dealing with such streams, leading to the use of RxJS. The fundamental concept of an Observable is introduced, described as a data wrapper that can be subscribed to, with changes in data notifying subscribers. The speaker also discusses the importance of importing only necessary RxJS classes and operators to minimize bundle size, and demonstrates creating a basic Observable and Subscription.
🔌 Creating Observables and Understanding Hot vs. Cold Observables
The speaker delves into creating observables from scratch, explaining the process of emitting data to subscribers. They discuss the concept of observables being completed, which stops further data emission. Various methods for creating observables are introduced, such as 'of' for raw values, 'from' for arrays, promises, or iterables, and 'fromEvent' for DOM events. The difference between 'of' and 'from' is clarified, with examples. The paragraph also touches on the creation of observables from time intervals. The concept of schedulers is briefly mentioned, allowing control over the timing of observable emissions. The distinction between hot and cold observables is explored, with examples demonstrating how cold observables generate values upon subscription, while hot observables can have multiple subscriptions. The use of 'share' and 'replay' operators to make cold observables hot is discussed, along with the introduction of Subjects and BehaviorSubjects, which allow for new values to be pushed to the stream after creation.
🛠 RxJS Operators for Data Flow Control
This section focuses on RxJS operators that control the flow of data through observables. The speaker demonstrates the use of the 'map' operator to transform emitted values and the 'scan' operator for accumulating values. The importance of the order of operators in a pipe is stressed, as it affects the outcome. The 'filter' operator is introduced to selectively emit values based on a condition. The 'take' operator is used to limit the number of emitted values, and the 'tap' operator is shown for triggering side effects within the observable pipe. The concept of back pressure, where an observable emits more values than needed, is addressed, with solutions like 'debounce', 'throttle', and 'buffer' operators provided to manage the flow. The speaker also introduces 'switchMap' for switching between observables based on data, and discusses combining observables using 'combineLatest' and 'merge'. Error handling in observables is touched upon with the 'catchError' operator, and strategies for preventing memory leaks, such as unsubscribing and using 'takeWhile', are discussed.
Mindmap
Keywords
💡RxJS
💡Observable
💡Subscription
💡Operators
💡Subjects
💡BehaviorSubject
💡Schedulers
💡Hot and Cold Observables
💡Reactive Programming
💡Backpressure
💡Memory Leaks
Highlights
RxJS is a powerful JavaScript library for handling asynchronous streams of data.
RxJS is more popular than Angular and only second to lodash in terms of daily npm downloads.
JavaScript alone doesn't provide everything needed to work with asynchronous data streams, which RxJS addresses.
Observables in RxJS are like pipes for data, allowing for subscription and notification of data changes.
RxJS is tree-shakable, meaning only the necessary code is imported, reducing bundle size.
Creating an observable involves calling it, which provides a callback function to notify subscribers of new data.
Subscriptions to observables are reactive, allowing for reactions to changes in observable data.
Observables can be completed, shutting them off and preventing further value emissions.
RxJS provides helper functions like 'of' to create observables from raw values or 'fromEvent' to create observables from DOM events.
Schedulers in RxJS can control whether observables are synchronous or asynchronous.
Hot and cold observables differ in their ability to have multiple subscriptions; cold observables create values upon subscription.
Subjects in RxJS are like hot observables with the ability to push new values to the stream.
Behavior subjects maintain a current value, ensuring that all subscriptions receive the last emitted value.
RxJS operators like 'map' and 'scan' can transform and accumulate values in an observable stream.
The 'filter' operator can be used to prevent certain items from being emitted in the stream.
The 'take' operator can control the number of values emitted from an observable before completion.
The 'tap' operator allows for triggering side effects within an observable pipe.
Back pressure in RxJS can be managed with operators like 'debounce', 'throttle', and 'buffer'.
The 'switchMap' operator allows switching from one observable to another based on a value.
Combining observables can be achieved with 'combineLatest' or 'merge', depending on whether you want to wait for all values or emit them as they come.
Error handling in RxJS can be managed with the 'catchError' operator, allowing for error interception and replacement with default values.
Memory leaks in RxJS can be prevented by unsubscribing from subscriptions or using operators like 'takeWhile'.
The 'takeUntil' operator can be used to stop a subscription when another observable emits a value.
Transcripts
[Music]
one of the most difficult but also most
rewarding javascript libraries to learn
is rxjs it's a tool that helps us
control data as it flows through the
dimension of time in today's episode of
code that's notthat you'll learn ten
fundamental concepts in rxjs and how to
avoid the bad stuff if you're new here
like and subscribe and leave me a
comment below for a chance to win this
t-shirt next week rxjs is getting pretty
popular these days in fact it gets more
daily npm downloads than angular
reactive view and the only functional
library that i'm aware of it's bigger is
low - and the reason it's so popular is
because javascript alone doesn't give us
quite everything we would like to work
with asynchronous streams of data we
have promises but they only work with a
single ASIC value so we use callbacks
for real time streams but you've
probably heard the term callback hell
rxjs addresses these issues and gives us
a powerful functional library for
dealing with strings and when I say
stream I'm talking about any data source
that unfolds over the dimension of time
things like data from firebase Dom
events WebSockets file uploads etc the
first thing you'll need to do is install
rxjs I'm currently running version 6.4
now I'm just using vanilla JavaScript
with webpack but feel free to follow
along with your favorite JavaScript
framework the most fundamental class in
rxjs is the observable you can think of
it as a wrapper for some data that can
be subscribed to and then the subscriber
will be notified anytime the data
changes when working with rxjs you
should think of yourself as a plumber
and I mean that in the most literal
sense because an observable is
essentially a pipe for data and rxjs
gives you all kinds of tools to modify
those pipes let's go ahead and create
our first observable but first we need
to import the rxjs library and there's a
right and a wrong way to do this
currently I'm doing it the wrong way by
using import star as our X but that's
going to cause the entire library to be
bundled up in our code you can see down
here we have a bundle size of 47
kilobytes but rxjs is a tree shakable
library which means we only have to
import the code that we actually need so
when working with rxjs you should only
import the classes and operators that
you actually need in your source code
and by doing that we reduce our bundle
size to less than 2 kilobytes now that
you know that let's go ahead and create
an observable from scratch now you won't
normally create observables like this in
your own code but it's a good exercise
to understand what an observable is when
we call observable
it gives us a callback function that we
can use to notify a subscriber with some
new data this can be synchronous or
asynchronous but basically every time we
call next it's like emitting an event to
the subscriber that they can listen to
and react to now that we have our
observable created we can create a
subscription to that observable by
calling subscribe on it
subscribe takes a function that will be
called every time the observable emits a
new value and this is why they call it
reactive programming because with your
subscription you're reacting to changes
in the observable data throughout this
video I'm using my own helper method
called print' which will react to each
newly emitted item and print it to the
Dom so we can visualize what's happening
in the observable and if we open this in
the browser you can see we get ABC
logged one after the other another
important concept to keep in mind is
that observables can be completed which
means they'll be shut off and no longer
a net values if we had observer complete
and then go back to the browser you can
see that only a and B are emitted and
that's because this stream has been
closed if you think like a plumber
creating the observable is like
connecting a pipe to a water source and
then subscribing is like opening the
valve that lets that water out rxjs
provides a bunch of helper functions
that make it easier to create
observables for example if we want to
just create an observable of a raw value
we can use of this we'll just take the
value that you pass in and wrap it in an
observable if we subscribe to this then
we'll just get hello printed out in the
console now the of function is very easy
to mix up with the from function which
takes an array promise or iterable and
then emits each individual item from the
observable we can see the difference by
passing in a string which itself is
iterable instead of emitting the entire
string as one event it emits each
individual character in that string
another thing we can do is create an
observable from events in the Dom using
from event we first pass in a Dom
element and in the event that we want to
listen to if we listen to clicks on the
document and subscribe to the observable
we'll get an event every time we click
on the page another thing we can do is
set up an observable based on a time
interval it takes the number of
milliseconds as its argument and then
will emit a number each time that
interval passes now one thing that a lot
of people don't realize is that rxjs can
be synchronous or asynchronous and
you'll probably never have to do this
but you can control that behavior by
modifying schedulers if you get lost in
this next example I would recommend
watching my async/await video which will
teach you all about the event loop and
JavaScript let's go ahead and define a
string observable using of
and then we'll just synchronously call
our print method with world in this case
rxjs is going to treat that observable
as asynchronous value so the
subscription will happen on the main
thread therefore we get hello world
printed one after the other you can
change this behavior by modifying the
schedule or of the observable if we pass
in the async scheduler it will emit the
value of the observable on the next
iteration of the event loop this time in
the browser we get world hello and again
you'll probably never need to mess with
that but I just want to give you an idea
of what schedulers do the next thing
we'll do is look at the difference
between hot and cold observables I don't
really like this terminology and I think
the best way to think of it is hot
observables can have multiple
subscriptions whereas cold observables
can only have one subscription cold
observables don't actually create the
underlying value until they're
subscribed to will go ahead and create
an observable from scratch and just have
it generate a random number now if we
subscribe to this observable twice
you'll see that we get two different
random numbers that's because that
create callback function isn't called
until the subscription is created but
often in the real world that's not what
you actually want a lot of times rxjs is
valuable because you can share a value
across multiple subscribers if you have
an existing cold observable there are
multiple ways to make it hot or in other
words allow it to be broadcast to
multiple subscribers now in this example
only the first subscriber will get the
actual value but what's often useful in
the real world is to use the share
replay operator which will cache the
last value as well if instead we pipe
and share replay one you can see that
both subscribers will get the same
random number now in my experience I'm
not usually taking cold observables and
making them hot instead I'm creating
subjects or behavior subjects you can
think of a subject as a hot observable
but it has the added benefit of being
able to have new values pushed to it in
plumbing terms it's more like a pump
where you can add new values to the
stream after it's been created we can do
this easily by just instantiating a new
subject and then we can subscribe to it
just like we've done with our other
observables what's unique about it is
that it has a next method that we can
call to add new values to the stream now
a potential gotcha with a regular
subject is that you need to have the
subscription setup before you start
adding values to it you can see here in
this first example we subscribe then we
add two new values to it and then we set
up a second subscriber and what you
intuitively might think is that both
subscribers will get the same values but
that's not true for the second
subscriber which will get nothing
because it's subscribed late after the
values were already at
and that brings me to one of the most
useful things in rxjs which is the
behavior subject it's similar to the
subject we just looked at except it has
the concept of a current value this
means the last emitted value will be
cached similar to how we set it up with
share replay earlier but most
importantly it means that every
subscription will always receive a value
even though our second subscriber came
in late it still gets the last emitted
value in the stream and that tends to be
a very powerful feature when doing
things like state management and
front-end applications at this point
we've only been talking about creating
observables now it's time to look at
operators which help you control the
flow of data going through your
observables there are a ton of operators
built into rxjs so we'll just look at a
handful of some of the most popular ones
first we'll create a source observable
the NIEM it's the integer is 1 through
10 you can compose multiple operators
together by using a pipe and just like
it sounds your data will flow through
this pipe and then be modified by each
function or operator in the pipe if we
subscribe to the empty pipe it just
emits the values of 1 through 10 to take
the input and simply transform it to a
new output you can use the map operator
for example we can take each number and
then raise it to the power of 2 now the
observable emits the square of the
initial numbers another thing we can do
is accumulate values as they flow
through the observable similar to a rate
reduce if we want to keep a running
total of all the values that were
emitted in this observable we can use
the scan operator and then add the
current value to the accumulated value
now each emitted value gets added on top
of the previous one an important thing
to keep in mind here is that the order
of operation matters for example if we
move the scan operator before the map
operator we're going to get different
results because the underlying math is
now different so that's how you
transform values but another useful
operator is filter which prevents
certain items from being emitted in the
stream for example if we only want
values that are greater than 10 we can
use the filter operator and that will
only emit values that meet this
condition and now we only see values
that are greater than 10 printed in the
UI another thing we can do with
operators is tell the observable to
complete for example if we pipe in take
3 it will only emit three values from
this observable then complete it the
next thing I want to show you is an
operator called tap which allows you to
trigger side effects from inside the
observable pipe in all the examples so
far I've been printing values to the
screen by using the callback in the
SUBSCRIBE method but that only gives us
access to the value at the end of the
pipe the most simple use case that
you'll find is just console logging at
different
points within the observable pipe the
pipe modifies things from top to bottom
so if we add a tap to the very beginning
it's going to print the initial value
and if we map it to something else and
add another tap after that it's going to
print a different value and you can use
tap to trigger more complex side effects
for example you might want to save a
value from the observable to a back-end
database if you're using something like
firebase you'll probably wanna use an
async function so basically it just
gives you a context to tap into the
observable and do something another term
you might hear with rxjs is back
pressure and basically that means you
have an observable that's emitting way
more values than you actually need we
can simulate this in the code by
listening to the mousemove event on the
Dom moving the mouse across the screen
will cause the observable to emit
multiple events every second rxjs has
several operators that can help us with
this situation the first strategy we'll
look at is to debounce the events ad
bounce will filter out all events until
they have stopped happening for a
certain period of time in this case
8,000 milliseconds ad bounce is really
useful for something like a type-ahead
where you don't want to make an API call
until the user is done typing you can
see here if we mouse around for a little
while nothing happens then if we stop
our event will be emitted after one
second
an alternative to ad balance is a
throttle it will emit the first value
but make sure that no additional values
can be emitted until a certain time
period has passed if we go back to the
demo and start moving around at the very
most we'll get one event emitted per
second these operators are alleviating
the back pressure but they're also
filtering out a lot of data if we want
to keep all the data but just not listen
to it all at once we can use a buffer
this will collect all the events into an
array and then only emit them when they
get to a length of 20 now we're going to
switch gears to another very important
operator called switch map this allows
you to start with one observable and
then switch to another one which is very
important when we're talking about
relational data for example if you're a
firebase user you probably have an
observable of a user logged into your
app and then you might want to switch to
another observable of some information
about that user in the database we can
simulate that in our code by creating an
observable of an object and then I'll
create a function that returns an
observable of some information based on
a user ID we can't call this function
until we have a user ID so the question
becomes how do we get the user ID out of
the observable the naive way to do this
is to nest subscriptions within each
other so first we subscribe to the user
that will give us the user ID then we
create another subscription inside of
that call
for the orders that code will actually
work but there's a better way to do this
instead we'll start with our user
observable to compose a new observable
called orders we first add a pipe to the
user and then we use the switch map
operator which will give us access to
the user ID and then we just returned an
observable from it and you can also
return a promise or an array here as
well so if you ever have one value that
depends on another value switch map
might be the operator that you're
looking for but there might be other
cases where you have multiple
observables that you just want to
combine into a single stream we'll look
at two different functions here called
combine latest and merge and keep in
mind these are not technically operators
so we import them directly from rxjs
I'm creating a cold observable of a
random number and then I'm creating a
second observable that just adds a delay
to that random number first we'll use
combined latest and it takes an array of
observables and it will wait for each
observable - you made a value and then
he met everything together as an array
even though the random numbers are going
to emit something right away it's going
to wait for that delay to resolve into a
value before it emits anything then if
any of the observables emit anything
after that it will emit everything again
as an array in other words it gives you
the current state of every observable in
this array so initially it's blank for
one second waiting for that delay and
then it emits out the four random
numbers in many cases though you don't
want to wait for that delay so instead
you can use merge which will just emit
each value one by one as it comes in
through the stream merge doesn't care
about the array position of the
observable it only cares about when it
emits it in the context of time so it
emits the first three values and in the
delayed value a second later another
thing you might be wondering is how to
catch errors in the context of a stream
you have a lot of flexibility here
but one of the most common strategies is
to catch the error and then replace it
with some other value we can do this
easily with the catch error operator it
can intercept the error and then you met
some other default value out of the
stream and one thing that's really nice
about rxjs is you can easily retry
things by just piping and the retry
operator I'm not going to get into it
here but it's super useful when working
with HTTP calls
first of all push a regular value and
then an error to the subject if we don't
catch the error
we'll get this uncaught error in the
console but by using the catch error
operator we can handle it in the
background and then provide some useful
information to the user and the front
end now if you're a good plumber then
you shouldn't have pipes that leak and
by that I'm talking about memory
weeks there are two main ways to prevent
memory leaks with rxjs
the first way is to unsubscribe from
your subscriptions take for example this
interval this interval is going to run
forever so if we don't close the
subscription it's just going to create a
memory leak in the background one
potential way to handle this is to
create a variable for the subscription
and then call subscription unsubscribe
at some other point in the code this
will stop the leak because we can see
here the observable only emits 10 values
but there's actually a better way we can
do this we can pipe in the take while
operator to the interval itself and that
will tell it to emit values only while a
certain condition is true when that
condition becomes false it will stop
your minion values therefore preventing
the memory leak and we don't need to
manually unsubscribe anywhere in our
code that works well in a lot of
situations but sometimes you want some
other observable or subject to be the
thing that triggers the subscription to
stop for that we have the take and tell
operator and it takes an observable as
its argument and when that observable
emits something it will cancel the
subscription on the source for example
if we wanted to set a timer for two
seconds on this interval we could do
that by using take and tell with a timer
observable when the timer finishes it
will complete the interval and then end
the subscription I'm gonna go ahead and
wrap things up there we barely just
scratch the surface with rxjs so let me
know what you want to see next in the
comments if this video helped you please
like and subscribe thanks for watching
and I will talk to you soon
[Music]
浏览更多相关视频
How to use RxJS with React Hooks
#68 What is an Observable | Understanding Observables & RxJS | A Complete Angular Course
When to use take() vs takeUntilDestroyed()?
Asynchronous JavaScript in ~10 Minutes - Callbacks, Promises, and Async/Await
Variable Basics (Updated) | Godot GDScript Tutorial | Ep 1.1
LabView Basics 2 - Writing data to an excel file
5.0 / 5 (0 votes)