‫Welcome back.
‫Let's now learn all about the event loop,
‫which is the heart of the Node.js architecture.
‫And this is probably the most important lecture
‫in this section, so make sure you really understand
‫everything that I show you during this video.
‫So, let's get started.
‫So, here is a diagram similar to the last lecture
‫so that we know exactly what we're talking about here.
‫So, we're still in a Node process in the single thread
‫where the event loop runs, all right?
‫Now, the first thing that you need
‫to know is that the event loop is
‫where all the application code
‫that is inside callback functions is executed.
‫So, basically, all code that is not top level code will run
‫in the event loop.
‫Some parts might get offloaded to the thread pool
‫as we saw in the last lecture,
‫but it's the event loop that takes care of all this.
‫As I said before, it really is the heart
‫of the Node architecture.
‫Okay, now, as I mentioned many times in the first part
‫of the course, Node.js is all built
‫around callback functions.
‫So, functions that are called as soon
‫as some work is finished some time in the future.
‫Remember that?
‫And it works this way because Node
‫uses an event-triggered architecture,
‫which is something that we're gonna talk about in one
‫of the next videos.
‫But what you need to know for now is
‫that things like our application
‫receiving an HTTP request on our server or a timer expiring
‫or a file finishing to read, all these will emit events
‫as soon as they are done with their work,
‫and our event loop will then pick up these events
‫and call the callback functions
‫that are associated with each event.
‫Okay, make sense?
‫So, again, the event loop receives events
‫each time something important happens,
‫and will then call the necessary callbacks
‫such as we define in our code.
‫So, in summary, it's usually said
‫that the event loop does the orchestration,
‫which simply means that it receives events,
‫calls their callback functions,
‫and offloads the more expensive tasks to the thread pool.
‫Now, how does all this actually work behind the scenes?
‫In what order are these callbacks executed?
‫Well, that is what we're gonna find out next.
‫So, remember, when we start our Node application,
‫the event loop starts running right away.
‫Now, the event loop has multiple phases,
‫and each phase has a callback queue,
‫which are the callbacks coming from the events
‫that the event loop receives,
‫just as we talked about in the last slide.
‫Now, in some places you will read
‫that there is only one callback queue or one event queue,
‫but in fact, as I said, the event loop has many phases
‫where each phase has its own callback queue.
‫So, let's now take a look at the four most important phases.
‫There are one or two other phases
‫that are used internally by Node,
‫but these are not that important
‫and I'm not gonna talk about them.
‫So, the first phase takes care
‫of callbacks of expired timers,
‫for example, from the setTimeout() function.
‫So, if there are callback functions from timers
‫that just expired, these are the first ones
‫to be processed by the event loop.
‫If a timer expires later during the time when one
‫of the other phases are being processed,
‫well, then the callback of that timer will only be called
‫as soon as the event loop comes back to this first phase.
‫Make sense?
‫And it works like this in all four phases.
‫So, callbacks in each queue are processed one
‫by one until there are no ones left in the queue,
‫and only then, the event loop will enter the next phase.
‫Next up, we have I/O polling and execution of I/O callbacks.
‫And again, remember that I/O stands for input/output.
‫So, polling basically means looking for new I/O events
‫that are ready to be processed
‫and putting them into the callback queue.
‫And remember that in the context of a Node application,
‫I/O means mainly stuff like networking and file access,
‫and so, it's in this phase where probably 99%
‫of our code gets executed,
‫simply because in a typical Node app,
‫the bulk of what we need to do is related to networking
‫and also, file accessing.
‫The next phase is for setImmediate callbacks,
‫and setImmediate is a special kind of timer
‫that we can use if we want to process callbacks immediately
‫after the I/O polling and execution phase,
‫which can be important in some more advanced use cases.
‫All right.
‫And finally, the fourth phase is for close callbacks,
‫which are, again, not that important for us,
‫but I put this here anyway for the sake of completeness.
‫Basically, in this phase, all close events are processed,
‫for example, for when a web server or a WebSocket shut down.
‫So, these are the four phases in the event loop,
‫but besides these four callback queues that we just saw,
‫there are actually also two other queues,
‫the nextTick() queue and the other microtasks queue,
‫which is mainly for resolved promises.
‫If you're not familiar with promises,
‫we will talk about them a bit in a later section.
‫Anyway, if there are any callbacks in one
‫of these two queues to be processed,
‫they will be executed right after the current phase
‫of the event loop finishes instead of waiting
‫for the entire loop to finish.
‫Okay?
‫So, in other words, after each of these four phases,
‫if there are any callbacks in these two special queues,
‫they will be executed right away.
‫Now, for example, imagine that a promise resolves
‫and returns some data from an API call while the callback
‫of an expired timer is running.
‫So, in this case, the promise callback will be executed
‫right after the one from the timer finishes.
‫Okay?
‫And the same logic also applies to the nextTick() queue,
‫which we didn't talk about yet.
‫So, basically, process the nextTick() is a function
‫that we can use when we really, really need
‫to execute a certain callback
‫right after the current event loop phase.
‫It's a bit similar to setImmediate,
‫with the difference that setImmediate only runs
‫after the I/O callback phase.
‫What is similar, though,
‫is that both are for really advanced use cases,
‫and we're probably not even gonna need them
‫throughout this course.
‫But anyway, I wanted to include this more complex stuff here
‫as well so that you have the tools
‫that you need if you really need
‫to dig deep into Node.js if you want to.
‫All right, and with that, we actually finished one tick
‫of the event loop, and a tick is basically just one cycle
‫in this loop.
‫So, now it's time to decide whether the loop should continue
‫to the next tick or if the program should exit.
‫And how does Node do that?
‫Well, it's very simple.
‫Node simply checks whether there are any timers
‫or I/O tasks that are still running in the background,
‫and if there aren't any, then it will exit the application.
‫But if there are any pending timers or I/O tasks,
‫well, then it will continue running the event loop
‫and go straight to the next tick.
‫So, for example, when we're listening
‫for incoming HTTP requests like we did
‫in our Node farm project in a previous section,
‫we were basically running an I/O task,
‫and that is why the event loop,
‫and therefore, Node.js, keep running
‫and keep listening for new HTTP requests coming in
‫instead of just exiting the application.
‫Also, when we're writing or reading a file
‫in the background, that's also an I/O task,
‫and so, it makes sense that the app doesn't exit
‫while it's working with that file, right?
‫Okay, and this is basically what you should know
‫about the Node.js event loop.
‫If you need even more detail than this,
‫well, you can always try to read
‫the official Node documentation, which should be quite easy
‫for you to understand at this point now
‫that you already understand most of the event loop anyway.
‫And I just wanna emphasize that it's really,
‫really important for you to correctly understand
‫the event loop so that you can write your own
‫performing code and also debug your own code
‫when something goes wrong in an unexpected way.
‫And now, just to finish, let's review some
‫of the stuff we talked about here.
‫So, in a nutshell, the most important thing
‫that I want you to understand from this lecture,
‫and maybe from this entire course,
‫is that event loop is what makes
‫asynchronous programming possible in Node.js,
‫making it the most important feature in Node's design
‫and making Node.js completely different
‫from other platforms.
‫It takes care of all incoming events
‫and performs orchestration by offloading heavier tasks
‫into the thread pool, and doing the most simple work itself.
‫Also, remember that we need the event loop
‫because in Node.js everything works in one single thread,
‫and so, you can have thousands or millions
‫of users accessing the same thread at the same time.
‫This makes Node so lightweight and scalable,
‫but at the same time, it comes with the danger
‫of blocking our single thread,
‫which would make the entire app slow
‫or even stop for all your users accessing the app, right?
‫Now, in other languages like PHP running on
‫an Apache server, basically, a new thread is created
‫for each new user, which is way more resource-intensive.
‫But on the other hand, there is no danger
‫of blocking, right?
‫So, that whole model makes it a bit easier
‫to use PHP for beginners, but of course,
‫it comes with its own disadvantages,
‫which I'm not gonna go into at this point.
‫Anyway, let me again remind you
‫that it's your responsibility to not block the event loop,
‫and so here's a couple of guidelines for that.
‫First off, don't use the sync versions
‫of functions in the fs, crypto,
‫or zlib modules in your callback functions, okay?
‫So, in our first project, we actually did use
‫the synchronous version, but it was in the top level code,
‫so outside of any callback.
‫And since that code runs before the event loop even starts,
‫well, it's not problem to use the synchronous version there.
‫Also, and this is probably pretty obviously,
‫don't perform very complex calculations in the event loop.
‫So, stuff like crunching millions
‫of numbers in loops inside of loops, or something like that.
‫Next, be careful with JSON in very large objects
‫because at some point, it can start to take a long time
‫to parse, or to stringify, JSON.
‫And finally, don't use all too complex regular expressions,
‫for example, with multiple nested quantifiers
‫or back references, because again,
‫they can take longer than expected.
‫These are, of course, just a couple
‫of high level guidelines, but they will get you started
‫on the right path.
‫Now, there are some potential solutions
‫to these blocking problems, like manually offloading
‫to the thread pool or using child processes,
‫and we might talk about this by the end of the course
‫or some time in the future, but for now,
‫it's important that you understand and follow this advice
‫of really not blocking the event loop.
‫All right.
‫Next up, I'm gonna give you a small example
‫to show you some of the stuff
‫that we talked about in practice.