© Kanika Sud 2020
K. SudPractical hapihttps://doi.org/10.1007/978-1-4842-5805-7_3

3. Asynchronous JavaScript

Kanika Sud1 
(1)
Kanika, Chandigarh, India
 

Now that we know the overview of Node.js from the last chapter, it’s time to get into some core programming concepts of the language that changed it all – JavaScript. JavaScript is deceptive in that its lightweight structure will make you feel that you’re indeed done with the whole language, because you have an app running and returning a response. If you dive in deeper – which is always a good thing in programming – JavaScript can offer a whole new world, where thorough concepts can make or break an app and its scalability. Let’s walk you through. We’ll just be explaining the most difficult parts of JavaScript pertaining to Node.js – for the most commonly understood sections, we’ll leave a trail of further reading and online recommendations.

Understanding Asynchronous Programming in General

Somewhere down the line, in data-intensive apps, you’ll realize you want to use resources in the most efficient manner, without blocking a handle to those resources. Now, when do such use cases come along? A massive number of users on the system at one time, intensive I/O operations, and so on. So what do we do? We get the privilege of initiating an operation and it getting handled when the resource is free. Compared to a multi-threaded environment, this brings in a much cleaner, single-threaded, non-blocking way of programming. Again, JavaScript in itself is not asynchronous; it supports asynchronous programming.

For those of us who’ve been novice developers in client servicing, we have never had the time to delve into the skin of things – it’s a little obscure region to talk about what blocking resources means. Well, time for some real-world examples again. Restaurants, anyone?

You have one kitchen and one chef. Even then, would you want your customers to wait for the first order to be processed and served before you can even take the second order? Better still, would you prefer a chef who starts preparing the second order once he takes a look at what’s in-line (the event loop), midway the first, and serves as and when prepared?
//asynchronous call
//whatever code is written here, won't wait for the asynchronous call.

Many browser functions are implemented asynchronously. Looking at JavaScript info (https://javascript.info/callbacks) would make our point clearer.

Now, JavaScript is a single-threaded language, and to get into blocking calls is very easy if not written properly. For a long time, asynchronous programming was served well by callbacks. The evolution of JavaScript saw promises and generators. We discuss callbacks next. We wish to point out to developers that just like JavaScript, callbacks are not asynchronous themselves. Callbacks can be used asynchronously. Whenever you revisit the topic, look for synchronous callbacks and asynchronous callbacks separately.

Understanding Callbacks

Callbacks are an example of first-class functions in JavaScript, where a function is passed to a function. To understand how they tie up with asynchronous programming, consider the difference between loading a file, reading a file, and some code below it (you don’t want that to wait) – what you do here is keep the loading and reading asynchronous, and the rest of the code does not depend on it.

Consider the following examples for loading a file with Ajax and a callback. Note that asynchronous behavior is inherent in Ajax. Callbacks here are demonstrated only for how they are used. The same example can be run without a callback, only that if you couldn’t assign callbacks or pass methods as parameters, you’d have to write the contained code over and over again, when instead all you have to do is just assign it as a callback and all your callback-contained code will be sorted in one go – in this case the innerHTML display (Listing 3-1).
<!DOCTYPE html>
<html>
<body>
<div id="demo">
<h1>An AJAX example with a callback function</h1>
<button type="button"
onclick="loadDoc('https://api.github.com/users?since=135', cb)">Change Content
</button>
</div>
<script>
function loadDoc(url, cFunction) {
  var xhttp;
  xhttp=new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      cFunction(this);
    }
  };
  xhttp.open("GET", url, true);
  xhttp.send();
  document.getElementById("demo").innerHTML = "<strong>Me First</strong>"
}
function cb(xhttp) {
  document.getElementById("demo").innerHTML =
  xhttp.responseText;
}
</script>
</body>
</html>
Listing 3-1

An Ajax Example with a Callback Function

Notice the following screens. The first screen (Figure 3-1) shows the program input, a basic button, which executes the script thereafter.
../images/494274_1_En_3_Chapter/494274_1_En_3_Fig1_HTML.jpg
Figure 3-1

Home Screen Change Content Through Callback

The program doesn’t wait to return the file. It prints Me First first, as shown in the following screenshot; and then when the file is loaded, it prints the response. Note that this is nonsequential, according to our code. The third screen shows the actual output that displays after some time.

../images/494274_1_En_3_Chapter/494274_1_En_3_Figa_HTML.jpg
../images/494274_1_En_3_Chapter/494274_1_En_3_Figb_HTML.jpg
Thus, delaying of execution has happened to when the data was available. Delaying of data can happen after some time has elapsed too. And that’s in case of the setTimeout() function . For now, just be sure you are clear on how asynchronous programming can alter the flow of a program. Try fiddling with the preceding code. For instance, we made another code piece without callbacks, strongly giving what asynchronous programming is (Listing 3-2).
<!DOCTYPE html>
<html>
<body>
<div id="demo">
    <h1>Check The Console</h1>
    <button type="button" onclick="loading('https://api.github.com/users/tom')">Check The Console
    </button>
  </div>
<script>
let script = document.getElementById('demo');
function loading(url) {
  loadDiv(url);
  console.log("Me Second");
}
function loadDiv(url){
  var xhttp;
  xhttp=new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
     console.log(xhttp.responseText);
    }
  };
  xhttp.open("GET", url, true);
  xhttp.send();
  console.log("Me First");
}
</script>
</body>
</html>
Listing 3-2

Code Without Callbacks

Listing 3-3 shows the output on the console.
Me First
Me Second
{
  "login": "tom",
  "id": 748,
  "node_id": "MDQ6VXNlcjc0OA==",
  "avatar_url": "https://avatars1.githubusercontent.com/u/748?v=4",
  "gravatar_id": "",
  "url": "https://api.github.com/users/tom",
  "html_url": "https://github.com/tom",
  "followers_url": "https://api.github.com/users/tom/followers",
  "following_url": "https://api.github.com/users/tom/following{/other_user}",
  "gists_url": "https://api.github.com/users/tom/gists{/gist_id}",
  "starred_url": "https://api.github.com/users/tom/starred{/owner}{/repo}",
  "subscriptions_url": "https://api.github.com/users/tom/subscriptions",
  "organizations_url": "https://api.github.com/users/tom/orgs",
  "repos_url": "https://api.github.com/users/tom/repos",
  "events_url": "https://api.github.com/users/tom/events{/privacy}",
  "received_events_url": "https://api.github.com/users/tom/received_events",
  "type": "User",
  "site_admin": false,
  "name": "Tom Malone",
  "company": null,
  "blog": "",
  "location": null,
  "email": null,
  "hireable": null,
  "bio": null,
  "public_repos": 11,
  "public_gists": 2,
  "followers": 23,
  "following": 3,
  "created_at": "2008-02-24T18:44:45Z",
  "updated_at": "2019-12-10T09:21:17Z"
}
Listing 3-3

Output Without Callbacks

What happened? The console dumps out the Me First, Me Second quickly, and then when the response is ready and is successful, it’s served, still maintaining sequential order. You could also add a timer to the code and see how many seconds elapse for what task – we leave that task to you.

Callback syntax can be confusing for non-JavaScript developers, and the way I keep track of it is to see that the callback receives its arguments – if any. For multiple callbacks, nested versions can become messy. This brings us to the concept of promises.

What’s in a Promise?

Coming back to the restaurant analogy, the manager or the cashier hands over an order number – which is a promise to deliver. What does it say? Collect your order when it is ready. Again, execution is not delayed after the first order is complete – we just say we deliver the order when it’s ready.

In other words:
//Prepare Order and Serve ->
    //Promise 1 - If Order1 is ready, serve order 1
    //Promise 2 - If Order2 is ready, serve order 2(Don't wait for order 1)

Again, we are achieving asynchronous results; and yes, the same can be written in callback language as well.

The syntax of a promise is
let promise = new Promise(function(resolve, reject) {
  // executor (the order function)
});
Its arguments resolve and reject are callbacks:
  • resolve (value) – If the job finished successfully, with result value.

  • reject (error) – If an error occurred. Error is the error object.

The state of the promise can be one of “pending,” “fulfilled,” or “rejected.” A promise returns the state and value, though state and value are not directly accessible.

When you use .then(), you’re telling your code to proceed if the resolution of the promise is successful. Through a series of .then() functions, called chaining of promises, we use the return value of one into the parameter value of another.

Listing 3-4 provides a new set of code for a simple demonstration of promises.
function loadDoc() {
  let url = 'https://api.github.com/users/tom';
  let response = fetch(url).then(function(response) {
      // response.json() returns a new promise that resolves with //the full response text
      // when it loads
      return response.json();
    })
    .then(function(json) {
      // ...and here's the content of the remote URL
      document.getElementById('demo').innerHTML = (JSON.stringify(json));
    });
}
Listing 3-4

Demonstrating Promises (script.js)

Listing 3-5 shows the index.html.
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="style.css">
    <script src="script.js"></script>
  </head>
  <body>
   <button onclick="loadDoc()">Demo</button>
   <div id="demo"></div>
  </body>
</html>
Listing 3-5

index.html

The fetch method returns a promise, the value of which is the response object if the promise is resolved.

If the promise is resolved, it returns a JSON representation of the response object. This is passed further down the line, through another successive then() method call. This way, we ensure that the JSON representation is passed only when it is completely resolved. That JSON representation is stringified and dumped into a div of your choice.

The preceding example is a live working example, and you can easily test it in a fiddle. It can be very easily modeled to a restaurant analogy as well. Your loadDoc() function would be a loadOrder() function. You could then serve the response as and when it is prepared (ready – status resolved, marked by your API). The idea is the same here – we make a promise to deliver when ready.

And if one order depends on, say, five dishes and you want to serve the order only when those five dishes are ready, you can even manage it by saying
let promise = Promise.all([...promises...]);
Promise.all([
  new Promise(resolve => setTimeout(() => resolve('OrderA'), 3000)), // 1
  new Promise(resolve => setTimeout(() => resolve('OrderB'), 2000)), // 2
  new Promise(resolve => setTimeout(() => resolve('OrderC'), 1000))  // 3
]).then(alert)

A detailed study of promises is out of the scope of this book. Refer to our recommendations in the “Further Reading” section at the end of the chapter. Especially focus on promisification if you want to understand how everything you do in callbacks can be converted to promise code. That’s for those who want to take their time on JavaScript and then reach Node.js.

async/await

One reason why we’re giving you a bite of JavaScript is because these four concepts of asynchronous programming are quite often used in Node.js, especially in hapi.js. You’ll see the await keyword in the server.js file, for instance, and to understand what’s going on with it, let’s understand how async and await help.

First, to the preceding code, add async and await and see how the behavior differs (Listing 3-6).
1.   async function loadDoc() {
2.   let url = 'https://api.github.com/users';
3.   try{
4.   const response = await fetch(url);
5.   const json = await response.json();
6.   document.getElementById('demo').innerHTML = (JSON.stringify(json));
7.   }catch(err)
8.   { console.log('fetch failed - async syntax', err);
}}
Listing 3-6

Code for Demonstrating async/await

Many users prefer this because it’s very similar to how we say things in other languages.

The best way to understand async behavior in the preceding piece of code is to remove await from line 5. Try saying const json = response.json();

You’ll get an empty brace {} on the screen. This shows us that if the response hasn’t loaded and we do not use the await keyword, it will not wait for the response to load and the control will move on.

Event Loop

We take up the event loop when we are discussing the entry point of a Node.js application in the chapters to come. Till then, know a very simple definition: here’s an endless loop, when JavaScript engine waits for tasks, executes them, and then sleeps waiting for more tasks.

Tying JavaScript with Node.js

In the beginning, before deep-diving into code, you must know that Node uses the Event-Driven Architecture: it has an event loop for orchestration and a Worker Pool for expensive tasks (includes I/O for which an operating system does not provide a non-blocking version, as well as particularly CPU-intensive tasks). The event loop will also fulfill the non-blocking asynchronous requests made by callbacks. While the event loop does not maintain a queue on its own, the Worker Pool does. When Node.js starts, it initializes the event loop, processes the provided input script (making async calls, scheduling timers), and then begins processing the loop. The loop in itself does not maintain a queue, but for a single-threaded language, it helps a lot by checking queues to see if it can take an input from a queue or a phase and process it.

A Worker pops a task from this queue and works on it, and when finished the Worker raises an “At least one task is finished” event for the event loop.

All in all, the modules of Node.js are very well equipped to handle asynchronous programming – which makes it a natural choice for mobile application APIs. You need to serve a lot of data in mobile applications, so quick-to-write and intricate tailoring becomes a good combination when serving data for scalable applications.

Hapi.js

Hapi.js is a lightweight framework which really helped me catch on to Node.js. At an architect level, you tend to see what platforms provide good flow, and hapi.js is one of them. It also keeps messy modules of Node at bay, while you can concentrate on developer-friendly, secure applications:
  • hapi offers a rich ecosystem. The plugins available have almost always covered every application design need.

  • The guarantee provided by hapi is very strong – for instance, the order in which components are configured is not a problem.

  • Plugins can safely rely on other plugins, including the order in which they must be executed.

  • Caches, plugins, decorators, and server methods are all protected and cannot be implicitly overridden – so you can say goodbye to the middleware hell. The fact that the framework itself has plugins makes the abstraction layer very flexible.

Summary

This chapter showed you some key concepts of JavaScript that might come in handy when understanding any framework in Node.js, because Node.js itself was adopted for modules of asynchronous programming. As we go forward, becoming clear with concepts and syntax of promises, async/Ajax will help, and remembering the callback pattern as the first approach to solve problems asynchronously will help in understanding why we write the code the way we do. This was more of a JavaScript primer, lest we face confusions in the keywords that might follow.

We also read about the event loop briefly and how it is used in Node.js. The event loop is what allows Node.js to perform non-blocking I/O operations – despite the fact that JavaScript is single-threaded – by offloading operations to the system kernel whenever possible.

We tied JavaScript’s asynchronous behavior to Node.js, and we saw how hapi.js is a lightweight framework to use Node.js. All in all, we have the right recipe for a quick RESTful Web Service app. As an exercise, try coding simple applications in callbacks and promises. See you in the next chapter.

Further Reading

https://javascript.info/promise-basics

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

..................Content has been hidden....................

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