This lesson is for PRO members.

Unlock this lesson NOW!
Already subscribed? sign in

Use RxJS Async Requests and Responses

7:14 RxJS lesson by

In this lesson we start building a small UI widget: suggestions box displaying users to follow in Github. We will learn how to perform network requests to a backend using RxJS Observables.

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

In this lesson we start building a small UI widget: suggestions box displaying users to follow in Github. We will learn how to perform network requests to a backend using RxJS Observables.

Avatar
Kevin

I don't understand why jQuery.getJSON was demonstrated, but not a plain 'ol xhr object;

Doesn't RxJS have a shorthand methods to handle AJAX requests, or I'm mistaking that capability only with something that Rx.DOM has (I know Rx.DOM does w/ things like Rx.DOM.get & RxDOM.jsonRequest; I don't know if Rx without Rx.DOM has 'em since 3.0?

I thought it was just demonstrated to also show it's possible w/ jQuery & to show off RxJS's fromPromise helper method, but realized a plain 'ol

In reply to egghead.io
Avatar
Joel

I'd push JQ out of your mind, as it is an unimportant detail 😜

It could be fetch, axios, etc...

In reply to Kevin
Avatar
Dean

Great job on the RxJS Tutorials.. I'd love to see some real practical examples for MySQL implementations...

In reply to egghead.io
Avatar
jvcjunior

Could this part of the code:
var responseStream = requestStream
.flatMap(requestUrl =>
Rx.Observable.fromPromise(jQuery.getJSON(requestUrl))
);

be replaced by:
var responseStream = Rx.Observable.fromPromise(jQuery.getJSON('https://api.github.com/users'));

?

Avatar
Andre

Hi jvcjunior. It would probably behave the same, but it's still different because the requestStream may emit many URLs over time, not just one hard-coded URL.

In reply to jvcjunior
Avatar
Rafael

I'm leaving here an example of this using RxJS v5.0.0-beta.10

import Rx from 'rxjs/Rx';
import fetch from 'fetch-jsonp';

const fetchEndpoint = url => fetch(url)
    .then(response => response.json())
    .then(response => response.data);

const request$ = Rx.Observable.of('https://api.github.com/users');
const response$ = request$
    .flatMap(url => Rx.Observable.fromPromise(fetchEndpoint(url)));

response$.subscribe((response) => console.log(response));

Please let me know if you see a problem with this implementation.

Avatar
Kostiantyn

How can I combine interval & request to the server with Observable? Could someone provide a hint?

Avatar
Andre

Hi Kostiantyn, you could accomplish that with another flatMap or with combineLatest. Here is how you could do it with flatMap (or a variant like switchMap/flatMapLatest):

var responseStream = Rx.Observable.interval(1000)
  .switchMap(i => requestStream)
  .switchMap(requestUrl => Rx.Observable.fromPromise($.getJSON(requestUrl)))

(switchMap is from RxJS v5. flatMap and flatMapLatest are from RxJS v4)

In reply to Kostiantyn

Now we will see how to use reactive programming in practice. We are going to use RxJS as the tool, which is one of the most popular reactive programming libraries out there. We will build a small piece of user interface.

This here is a suggestions box showing a couple of GitHub users that you could follow. It has these features. On start up, it loads accounts from the API, and it displays three suggestions. Each of these rows is a user with their avatar and their username. When you click refresh, it should replace these users with more, three users, and if you click X, then it replaces that user with another suggested user to follow.

Let's start with the easiest feature, which is, on start up, load three user accounts from the back end. Now, this is really just about doing a request, getting a response, and rendering that response. We need somehow do that with event streams, and let's just start by representing our requests, which are an event stream of strings.

These strings, I mean the URL of GitHub's user endpoint. This is just a stream of strings. It does really nothing. We need to somehow make something happen when this string is emitted. We do that by subscribing to that stream, and we get the request URL that the stream emits, and we do something inside here.

How do we normally make network requests in JavaScript? Well, one of the options is to use jQuery to get a promise from this URL. Now this will return us a promise, and once you have the promise, you can give a "Done handler" for the response. Now, inside here, we can, for instance, console log that response.

But, wait a second. Why are we using this way of making network requests if observables can handle asynchronous data streams? Aren't these supposed to already handle these? Why are we using the solution that doesn't look like event streams?

We can try doing this with event streams by creating a response event stream. The way we do that is by wrapping that promise from jQuery inside an observable like this. What we have here is a response stream that will be created from that promise, and now we can subscribe to that response stream and console log that.

If you look at this, it means that the observable can do whatever the promise is doing. Does this mean that a promise is like a simplified version of an observable? You can put one thing in the other. In a way, yeah. Promises are basically a simplified version of an event stream that can have only one value or only one event, which is either the resolved value or an error. They can have only one.

Observables go beyond that. Of course, they can have just one event happening on them, but they can also have multiple. That's why observables are more powerful than promises because they can have multiple events. This is pretty nice, and it shows how observables are more powerful.

We still have a problem with our example here, because we have a subscribe inside another subscribe and this reminds us of callback hell. We don't want to go to that direction, definitely. How can we solve this?

We know that Rx observables have operators such as map and things like that. What if we were to try mapping the request to the response? That makes some sense. Let's create a response stream that will be the request stream mapped. It means that we're mapping each of these request URL strings to...

Well, we need to map it somehow to the response, and the only way that we can get a response is by waiting for that network request to happen. I just wrote this, but I will explain what it means. Usually, we are mapping from a string to a number or a string to an object or something. Now we have to map from a string to something that will happen later in time.

Usually, we don't map from simple objects and numbers to observables, but this is what we're doing here now. We're mapping to an observable. This looks a bit different to what you're used to, because this is what the marble diagram would look like. This yellow circle is the request URL string, and it's being mapped to an observable of that response. This is what we're doing here.

What you get, as a result, is basically an observable of observables and we call this sometimes meta stream. You are probably confused right now, and that's OK. We normally don't work with this, because gladly we have a way of solving this, and that is by calling flatMap. When we call flatMap, instead of getting an output that looks like this, we get an output that looks flat, like that.

What's flatMap do? It basically flattens the meta stream that we got as output here, after it maps. That's basically what it does. In other words, it's emitting on the trunk stream everything that happens on these branch streams, and then it looks nice. flatMap is kind of like promise.then. You can think of it as that.

Now that we have this response stream built from the request stream, we can just subscribe to that response stream and we can put it in the console. Now we have only one subscribe. We don't have that mess of a subscribe inside another subscribe. And, this works.

That's part one. Keep on watching, and we're going to see how to build the whole user interface.

HEY, QUICK QUESTION!
Joel's Head
Why are we asking?