I bet you’d love NodeJS — Part 2

This series of articles are written as notes and help for participants of the workshop that will be held in OKE Software in Gdańsk Poland. It is by no means a comprehensive guide to the NodeJS technology itself.

Before I started writing this series of articles, I asked my friends/coworkers about the concepts that they would like to read. One of the concepts that were popping most often was the information about how does Node engine actually works in terms of asynchronicity.

Let’s start with an example. In example/async.js write a simple piece of code:

We are using setTimeout function. This function isn’t actually in the core of the JS. It is provided by the WebAPI (in the browser) or NodeAPI. It enables us to provide a function (first parameter) that should be executed after given amount of milliseconds (second parameter). Since we are passing the second argument equal to zero, our intuition might prompt us that the order of the execution should be:

first()
second()
third()

If we actually run it, we get:

Not what we expected, isn’t it?

Yeah. To wrap our head around it, we must actually explain few concepts:

Call stack — is a structure holding the trace of all the calls between the functions in our program. The function is added to the top of the stack when it is called and removed from it when it returns. If we write example like this ( example/trace.js ):

We can clearly see the trace for given place in our code using console.trace :

We see that calling console.trace from getMeANumber is showing the path of calls from function to function to this exact place. Also we don’t see getMeANumber on the stack with trace from thisIsTheLastFunctionOnStack as the getMeANumber function already finished its execution by the time we call thisIsTheLastFunctionOnStack.

Here’s another example to help you illustrate the idea:

https://i.ytimg.com/vi/pf-VnHnxfLk/maxresdefault.jpg

And the actual video it was taken from:

That was rather simple, but to fully understand how does Node handles async code we must introduce Event Loop and Callback Queue .

And then we can create example/request.js :

One thing that we have to keep in mind is that JavaScript is single threaded, meaning that call stack can run just one thing at the time. If we run this example we’ll get:

Let me walk you through what actually happens. This is the state of our app as we run it:

The JS doesn’t really have the concept of themain function, but for demonstration purposes let’s use that name to show the execution of our app.

First, we run the console.log('This is the very start') it gets on the call stack, gets executed and immediately goes away:

Next, we call setTimeout, which comes from Node API. Calling it will register the event callback in NodeAPI. It will wait two seconds until the callback execution:

After registering the callback setTimeout statement finishes and gets thrown off the stack. Then the registered event starts counting down. While it does the call stack can continue:

Next, we use request library to perform an AJAX call:

By its nature we don’t know when it finishes, so we register a callback function (second parameter) to get information about the response from the API that we sent the request to. Of course, after the registration the request call gets removed from the call stack:

What happens next depends on whether the request comes back first or 2 seconds passes first. As we saw in our example the http GET call was finished first. The callback doesn’t fire right away even after given time passes or event (response getting back to us from server) occurs. It gets pushed to Callback Queue :

request callback pushed to callback queue

The thing is the functions in a callback queue can be executed ONLY if call stack is empty and they execute one at the time. The event loop is actually the mechanism that manages the synchronization between call stack and callback queue. It checks whether there’s nothing going on in the call stack and only if it’s empty it will take first callback from the queue and execute it. For now, let’s assume that the main hasn’t finished yet and it still needs to execute the second console.log. Therefore event loop can’t do anything since call stack isn’t empty yet:

But after console.log gets executed the main flow ends and call stack is empty:

Empty call stack after the execution of last console.log

This is the signal for event loop to actually start executing the callbacks from the queue:

Moving the first callback to the call stack

After first callback executes there’s nothing in callback queue and nothing in call stack:

But we still have one callback registered and counting down, so the Node process won’t finish. When the counting down finishes second callback will move to callback queue:

Second callback in the callback queue

Event loop will see that call stack is empty and the callback can be moved to call stack for execution:

Of course, if our request had taken longer than two seconds the timeout callback would get executed as first.

Async Patterns in JS

With callback queue explained we can move to discuss the ways that we can perform asynchronous tasks in JavaScript. The most trivial and the easiest approach is to use callbacks. We’ve seen it already above, but let us take another look at it. To illustrate this example I will use open API that can be found here:

It’s an API with Chuck Norris jokes. They never grow old.

As you can see callback is just a function passed into another function that should be executed somewhere during the first function execution. From my personal experience, this is the worst possible case of handling async code. Why?

  • Because you lose control of the execution. The function that gets the callback isn’t really bound to call it:

Oops. I changed my function that takes callback a little bit. Guess what? Your callback won’t get fired even if you actually expect it to.

  • The readability of the code is horrible. Nesting function inside a function inside a function. When you need to perform another request that is dependent on the data from the previous request the maintainability of this code quickly goes to hell.
  • Error handling. Imagine that you have a completely different path of requests to perform once you hit an error in any previous request. Can you imagine debugging that?

Thankfully in JS, we have Promises. What are those? Simple analogy would be going to a McDonald’s and ordering a burger.

Do you get one instantly? Not that I’m aware of. You most likely get a receipt, which is a proof, form of contract saying that you actually ordered a burger and you’ll get one eventually. How does the process of ordering a burger look?

  • You place your order
  • You are informed that your request has been taken, you get your receipt and wait for the meal
  • If everything goes well then you eventually get your yummy burger
  • Unfortunately, you must be prepared for the scenario when there’s some problem with delivering you request

Let’s take a look at what the Promise is in JavaScript world. Promises are build using new keyword, so they’re built using constructor function and take another function as a parameter.

The params of the function passed into the promise are resolve and reject and are used to inform whether the operation performed by the promise was success or failure. You can get the data (the result of your operation) from the promise if you use then function on it:

The problem is then will get called only if the promise is fulfilled correctly. Meaning there are no errors during its execution. How about handling those? If somewhere along the execution of the promise reject will get called the promise will return an error that we can catch:

So the Promise can go from pending state into resolved or rejected state:

What’s great about the promise is that it can only change its state once. When the promise gets to rejected/resolved state it is done, it won’t get fired again. It’s different from using callbacks where you can get you callback function accidentally triggered twice or three times!

It won’t be the case for the promises:

Another thing that I’d like to show you is how to use promise with input arguments and how to chain promises. One thing that we love to use promises for are requests and as you can imagine we need somehow to pass url for the request into the promise. Let’s actually try and write the function that will use request library, but we will promisify it for our convenience. To do that we’ll write requestPromise function that will take url as an argument and return new Promise :

Again we use reject and resolve to decide in what state our promise should be and when. Now that we have that out of our way, we can rewrite our callbackHell function using Promises:

The concept is rather simple really. As long as you return promise in then of another promise you can chain on that. No nesting. Easy to read, easy to maintain. That’s here is a quick glance over Promises. They are functional in nature, they can be chained and they’re way better solution than callbacks.

I still wonder why Swift doesn’t have promises in the standard library.

Async/Await

So Promises are good and you can use them to solve most of your problems, but there are still caveats that could be handled better.

  • They rely on arrow functions, so it’s hard to debug them
  • Nesting conditionals inside promises isn’t readable

Also, we are really comfortable with the natural synchronous flow of the functions. What if we could actually get the both of both worlds? The code working asynchronously like in promises, but looking like normal synchronous code. Behold async/await syntax.

Let’s take a look:

To make a function asynchronous we simple add async keyword before function keyword or if we are using arrow functions we can add async before the arguments list. Async/await relies heavily on the promises concept. Every async action in the function must have await keyword added, which will stop the execution until given promise resolves. So what’s so wonderful about that?

Every async function returns a promise automatically. Just look how much less code did we write!

We don’t need to chain promises anymore. We can just write our code like it was synchronous.

Also we can use the pattern we already know to handle the errors. Good old try/catch block:

What is my personal take on this? I believe that the syntax is simple and concise, but it has its drawbacks. For example, most of the people are familiar with Promise syntax and they don’t see automatically that given function is asynchronous. I guess you just need to get used to that. It should be rather easy for people coming to JS from C#, which has async/await syntax built in for some time now.

Also even though you technically write less code, you loose the feeling of being in a functional world. For me chaining and using monad (a promise is one) feels really good.

Nevertheless, I will be trying to use async/await as much as possible for this workshop.

That’s it. I’ll see you in next one!

--

--

--

We create new multi-screen looks and functionalities including software for TV platforms, hybrid TV services and other features such as VOD, EPG, Catch-up TV, social media sharing, and mobile device functions.

Recommended from Medium

What is hoisting in JavaScript? What is the difference between undefined and not defined?

Build own Telegram Bot with Node.js

State: React Hooks Or React Classes

12 JavaScript Features You’ve Probably Never Used

How to build a Carousel from scratch

Another ExpressJS API tutorial for 2020, part 05 — Configuring services

Coloring an icon via css

Something new I’ve learned about javascript

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Wojciech Bilicki

Wojciech Bilicki

Mobile & FrontEnd Developer at OKE Software Poland. Marvel fan. Can't live without music and books.

More from Medium

Jest — Filenames without test or spec

Firebase Cloud Functions ~ Migrating an Existing JavaScript Project to TypeScript

Easiest way to create secure and random strings for your JWT, session and cookie secrets.

Set up your React app for CRUD operations using MongoDB Realm GraphQL — Part 2