‫Let's now take a minute to learn about
‫the asynchronous nature of Node.js,
‫which includes absolutely fundamental topics,
‫like synchronous, asynchronous, blocking,
‫and non-blocking code.
‫And all of this will be really important
‫in order to understand everything that's coming up
‫throughout this section.
‫So this piece of code that we wrote in the last lecture,
‫to read a file and then, save it's content into a variable,
‫was in a so-called synchronous way,
‫which simply means that each statement
‫is basically processed one after another, line by line.
‫In this example,
‫first, the file system module is required,
‫then, the file is read,
‫and then, we log the result to the console.
‫So you see that each line of code
‫basically waits for the result of the previous line.
‫Now, this can become a problem,
‫especially with slow operations,
‫because each line blocks the execution
‫of the rest of the code.
‫And so, we say that synchronous code
‫is also called blocking code because, again,
‫a certain operation can only be executed
‫after the one before has finished.
‫And because of the way Node.js was designed,
‫this turns into a huge problem, as we'll see in detail
‫in the next slide.
‫So the solution to this problem in Node
‫is to use asynchronous, non-blocking code.
‫So in asynchronous code, we upload heavy work
‫to basically be worked on in the background.
‫And then, once that work is done,
‫a callback function that we register before
‫is called to handle the result.
‫And during all that time,
‫the rest of the code can still be executing
‫without being blocked by the heavy task,
‫which is now running in the background.
‫So what this means is that we can effectively defer
‫or reaction into the future
‫in order to make the code non-blocking
‫and this is, of course, much better.
‫Makes sense?
‫So, in this example, we use the asynchronous
‫readFile function, which accepts a callback function.
‫This will start reading the file in the background
‫and then, immediately move on to the next statement,
‫printing to the console the string-reading file.
‫So, again, you see, we are not blocking the execution here.
‫Then, when the file is finally completely read,
‫the callback function will be called,
‫and so, the data that was read
‫will then be printed to the console.
‫So that's quite different
‫from the synchronous version, isn't it?
‫Now, the question here is,
‫why does is actually have to be this way?
‫What's the problem with blocking code execution in Node.js?
‫Or, in other words, why do we actually use callback
‫so many times in Node.js?
‫Well, let's find out.
‫And in order to understand these questions,
‫the first thing that we need to understand
‫is the fact that a Node.js process,
‫which is where our application is running,
‫there's only one single thread.
‫And the thread is just like a set of instructions
‫that is run in the computer's CPU.
‫So basically, the thread is where our code
‫is actually executed in a machine's processor.
‫So, remember, Node.js is basically single-threaded
‫and so, for each application, there's only one thread.
‫That's just the way Node.js was designed.
‫Now, what that means is that all the users
‫accessing your application are all using the same thread,
‫so, basically, accessing the same thread.
‫And so, whenever they're interacting with the application,
‫the code that is run for each user
‫will be executed all in the same thread at the same place
‫in the computer running the application.
‫And that is true no matter if you have
‫five users, like in this diagram, or 5,000 or 5 million.
‫All right?
‫Now, what this also means is that when one user
‫locks the single thread with synchronous code,
‫like we just saw before,
‫then all other users will have to wait
‫for that execution to finish.
‫And that might not be a huge problem
‫if you have like five users,
‫but it definitely will for thousands or even millions
‫of users at the same time.
‫So, imagine there's a user accessing your application
‫and there's a huge synchronous file read in your code
‫that will take like one second to load.
‫This will mean, of course, that for that one second,
‫all other users will have to wait
‫because the entire execution is blocked for that one second.
‫So if those other users want to do some simple tasks,
‫like logging into your application
‫or just requesting some data, they won't be able to do so.
‫They will have to wait until the file is finished reading.
‫Only when that happens they will finally be able to perform
‫the simpler tasks, one after another.
‫Now, please note, that this is a very oversimplified version
‫of what really happens behind the scenes of Node.js,
‫which is why we will come back to all of this
‫in the next section and get an even deeper understanding
‫of how Node.js handles asynchronous code under the hood.
‫But at this point,
‫this is enough for you to understand the concept.
‫It's better to go step-by-step here
‫and not make it too confusing
‫right from the beginning, okay?
‫Anyway, this is how the situation
‫would play out with synchronous blocking code,
‫which is obviously a terrible experience for your users.
‫And so, it's really your job as a developer
‫to avoid these kinds of situations
‫by using asynchronous code.
‫So, for the same situation, we should, of course,
‫use the asynchronous file read function,
‫which instead of blocking the single thread,
‫does the heavy work in the background,
‫where it basically stays until it's finished
‫reading the data from the file.
‫Of course, we then also register a callback function
‫to be called once the data is available.
‫And in this scenario, all the other users
‫can then perform their tasks in a single thread,
‫one after another, while the file is still being read
‫in the background.
‫Now, once the data is read, our callback function will,
‫of course, get called to be executed
‫in the main single thread in order to process the read data.
‫And that's it.
‫That's an overview of how Node.js
‫handles asynchronous behavior in order to implement
‫the non-blocking I/O model that we talked about
‫in the intro lecture, all right?
‫And I/O simply stands for input-output,
‫which is basically stuff like accessing the file system
‫and handling network requests.
‫This is actually the whole reason why Node.js
‫is completely designed around callbacks,
‫as you will see throughout the course.
‫In other programming languages, like PHP,
‫it works very differently because you get, basically,
‫one new thread for each new user,
‫which is a completely different paradigm
‫and really works completely different.
‫But the creator of Node.js found this model
‫to be the best solution for building highly performant
‫and scalable web applications.
‫Now, just as a final note here, it's important to know that,
‫when we use callbacks in our code,
‫that doesn't automatically make it asynchronous, all right?
‫So, passing functions around into other functions
‫is quite common in JavaScript,
‫but of course, again, that doesn't make them
‫asynchronous automatically, okay?
‫It only works this way for some functions in the Node API,
‫such as the readFile function
‫and many, many more, as people explore in the future.
‫And now, just to finish,
‫since we're talking about asynchronous code here,
‫just one last note about callback functions.
‫So, this callback model that we just discussed,
‫where one function is called
‫once the one before has finished it's work,
‫can quickly lead to some hard to read and unmanageable code.
‫Just take this example where the second file read
‫depends on the first one,
‫then, the third file read depends on the second one,
‫and finally, we want to use the final data
‫to write a file as a result.
‫That looks quite confusing, right?
‫I mean, it's gonna work just fine,
‫but it's just hard to reason about
‫and that is just with four levels deep.
‫Imagine you had like 10 or 20 levels,
‫which is actually not that uncommon.
‫Anyway, this is what we call the callback hell.
‫It's such a common problem,
‫that it already got its own name.
‫And do you notice this triangular shape here?
‫That's a very clear sign that you're in callback hell.
‫Now, how do we actually escape callback hell?
‫Well, we can use more advanced tools for handling
‫asynchronous code, like ES6 promises
‫or even better, ES8 async/await.
‫Now, the model that we just talked about
‫will still be the same.
‫We just have more elegant ways
‫of dealing with the code itself and writing it.
‫And there is a whole optional section
‫of it later in the course on promises and also, async/await,
‫so in case you're not familiar with them.
‫But for now, we will keep using callbacks
‫because that is what Node originally used
‫and was designed around.
‫And now, with that being said,
‫let's move on and use this asynchronous model
‫in practice for the first time.