Cascading calls

So far, we have covered the two main ways you will use AJAX to send or receive data. When it comes to receiving data, it's usually not as simple as fetching the data and rendering it. In fact, you will most likely have a dependency on when you can fetch which data. A typical example of this is needing to perform a login call before you can fetch the remaining data. In some cases, it might be that you need to first log in, then fetch the data of the logged in user, and once you have that you can fetch messages, orders, or whichever kind of data you need that might be specific to a certain user. This whole phenomenon of fetching data in this way is called cascading calls.

Let's have a look at how we use cascading calls with Promises and gradually learn how to do the same with RxJS. We are taking this little detour as we assume that most of you reading this book are familiar with Promises.

Let's look at the dependent case we first mentioned, where we need to perform the following steps in this order:

  1. The user first logs in to the system
  2.  Then we fetch information about the user
  3. Then we fetch information about the user's orders

Using promises, it would look something like this in code:

// cascading/cascading-promises.js

login()
.then(getUser)
.then(getOrders);

// we collect username and password from a form
const login = (username, password) => {
return fetch("/login", {
method: "POST",
body: { username, password }
})
.then(r => r.json())
.then(token => {
localStorage.setItem("auth", token);
});
};

const getUser = () => {
return fetch("/users", {
headers: {
Authorization: "Bearer " + localStorage.getToken("auth")
}
}).then(r => r.json());
};

const getOrders = user => {
return fetch(`/orders/user/${user.id}`, {
headers: {
Authorization: "Bearer " + localStorage.getToken("auth")
}
}).then(r => r.json());
};

This code describes how we first log in to the system, using the login() method, and obtain a token. We use this token in any future calls to ensure we make authenticated calls. We also see how we perform the getUser() call and obtain a user instance. We use that same user instance to perform our last call, getOrders(), whereby the user ID is used as a routing parameter: `/orders/user/${user.id}`

We have shown how to perform cascading calls using promises; we did this to establish a common ground for the problem we are trying to solve. The RxJS approach is very similar: we have shown that the ajax() operator exists and makes our lives easier when dealing with AJAX calls. To achieve the cascading calls effect with RxJS, we simply need to use the switchMap() operator. This will lead to our code looking like this:

// cascading/cascading-rxjs.js

let user = "user";
let password = "password";

login(user, password)
.switchMap(getUser)
.switchMap(getOrders);

// we collect username and password from a form
const login = (username, password) => {
return Rx.Observable.ajax("/login", {
method: "POST",
body: { username, password }
})
.map(r => r.response)
.do(token => {
localStorage.setItem("auth", token);
});
};

const getUser = () => {
return Rx.Observable.ajax("/users", {
headers: {
Authorization: "Bearer " + localStorage.getToken("auth")
}
}).map(r => r.response);
};

const getOrders = user => {
return Rx.Observable.json(`/orders/user/${user.id}`, {
headers: {
Authorization: "Bearer " + localStorage.getToken("auth")
}
}).map(r => r.response);
};

We have highlighted the parts that need changing in the preceding code. In short, the changes are:

  • fetch() is replaced by the ajax() operator
  • We call .map(r => r.response) instead of .then(r => r.json())
  • We do .switchMap() calls for each cascading call instead of .then(getOrders)

There is one more interesting aspect that we need to cover, namely that of parallel calls. When we fetched the user and the order, we waited for a previous call to fully complete before we initiated the next call. In a lot of cases, this might not be strictly necessary. Imagine that we have a similar case to the previous one, but there is a lot of interesting information surrounding the user that we want to fetch. Instead of just fetching orders, the user might have a friends collection or a collection of messages. The precondition for fetching that data is only that we fetched the user, so we know which collection of friends we should query for and which collection of messages we need. In the world of promises, we would use the Promise.all() construct to achieve parallelization. With that in mind, we update our Promise code to look like this:

// parallell/parallell-promise.js

// we collect username and password from a form
login(username, password) {
return new Promise(resolve => {
resolve('logged in');
});
}

getUsersData(user) {
return Promise.all([
getOrders(user),
getMessages(user),
getFriends(user)
// not implemented but you get the idea, another call in parallell
])
}

getUser() {
// same as before
}

getOrders(user) {
// same as before
}

login()
.then(getUser)
.then(getUsersData);

As we can see from the preceding code, we introduce the new getUsersData() method, which fetches orders, messages, and friends collections in parallel, making our app responsive sooner, as the data will arrive sooner than if we just fetched it one after another.

We can easily achieve the same thing with RxJS by introducing the forkJoin() operator. It takes a list of streams and fetches everything in parallel. We therefore update our RxJS code to look like the following:

// parallell/parallell-rxjs.js

import Rx from 'rxjs/Rx';
// imagine we collected these from a form
let user = 'user';
let password = 'password';

login(user, password)
.switchMap(getUser)
.switchMap(getUsersData)

// we collect username and password from a form
login(username, password) {
// same as before
}

getUsersData(user) {
return Rx.Observable.forkJoin([
getOrders(),
getMessages(),
getFriends()
])
}

getUser() {
// same as before
}

getOrders(user) {
// same as before
}

login()
.then(getUser)
.then(getUsersData);
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.85.178