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:
If we actually run it, we get:
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 (
We can clearly see the trace for given place in our code using
We see that calling
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
Here’s another example to help you illustrate the idea:
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
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 the
main 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 :
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:
console.log gets executed the main flow ends and call stack is empty:
This is the signal for event loop to actually start executing the callbacks from the queue:
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:
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
API | The Internet Chuck Norris Database
The jokes are available at Use HTTP GET to retrieve what you want (read on below for more details). Results can…
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
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
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.
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
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
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!