1 00:00:01,300 --> 00:00:03,370 Let's now take a minute to learn about 2 00:00:03,370 --> 00:00:06,370 the asynchronous nature of Node.js, 3 00:00:06,370 --> 00:00:09,510 which includes absolutely fundamental topics, 4 00:00:09,510 --> 00:00:13,010 like synchronous, asynchronous, blocking, 5 00:00:13,010 --> 00:00:15,140 and non-blocking code. 6 00:00:15,140 --> 00:00:17,810 And all of this will be really important 7 00:00:17,810 --> 00:00:21,090 in order to understand everything that's coming up 8 00:00:21,090 --> 00:00:22,503 throughout this section. 9 00:00:24,240 --> 00:00:27,620 So this piece of code that we wrote in the last lecture, 10 00:00:27,620 --> 00:00:31,830 to read a file and then, save it's content into a variable, 11 00:00:31,830 --> 00:00:34,400 was in a so-called synchronous way, 12 00:00:34,400 --> 00:00:36,840 which simply means that each statement 13 00:00:36,840 --> 00:00:41,330 is basically processed one after another, line by line. 14 00:00:41,330 --> 00:00:42,540 In this example, 15 00:00:42,540 --> 00:00:45,630 first, the file system module is required, 16 00:00:45,630 --> 00:00:47,630 then, the file is read, 17 00:00:47,630 --> 00:00:50,900 and then, we log the result to the console. 18 00:00:50,900 --> 00:00:53,340 So you see that each line of code 19 00:00:53,340 --> 00:00:57,340 basically waits for the result of the previous line. 20 00:00:57,340 --> 00:00:59,440 Now, this can become a problem, 21 00:00:59,440 --> 00:01:01,500 especially with slow operations, 22 00:01:01,500 --> 00:01:04,190 because each line blocks the execution 23 00:01:04,190 --> 00:01:05,710 of the rest of the code. 24 00:01:05,710 --> 00:01:08,120 And so, we say that synchronous code 25 00:01:08,120 --> 00:01:12,290 is also called blocking code because, again, 26 00:01:12,290 --> 00:01:15,080 a certain operation can only be executed 27 00:01:15,080 --> 00:01:17,740 after the one before has finished. 28 00:01:17,740 --> 00:01:20,850 And because of the way Node.js was designed, 29 00:01:20,850 --> 00:01:24,220 this turns into a huge problem, as we'll see in detail 30 00:01:24,220 --> 00:01:26,190 in the next slide. 31 00:01:26,190 --> 00:01:28,500 So the solution to this problem in Node 32 00:01:28,500 --> 00:01:32,160 is to use asynchronous, non-blocking code. 33 00:01:32,160 --> 00:01:35,380 So in asynchronous code, we upload heavy work 34 00:01:35,380 --> 00:01:38,470 to basically be worked on in the background. 35 00:01:38,470 --> 00:01:40,820 And then, once that work is done, 36 00:01:40,820 --> 00:01:43,370 a callback function that we register before 37 00:01:43,370 --> 00:01:45,730 is called to handle the result. 38 00:01:45,730 --> 00:01:47,540 And during all that time, 39 00:01:47,540 --> 00:01:50,380 the rest of the code can still be executing 40 00:01:50,380 --> 00:01:52,910 without being blocked by the heavy task, 41 00:01:52,910 --> 00:01:55,820 which is now running in the background. 42 00:01:55,820 --> 00:01:59,520 So what this means is that we can effectively defer 43 00:01:59,520 --> 00:02:01,620 or reaction into the future 44 00:02:01,620 --> 00:02:04,530 in order to make the code non-blocking 45 00:02:04,530 --> 00:02:07,676 and this is, of course, much better. 46 00:02:07,676 --> 00:02:09,287 Makes sense? 47 00:02:09,287 --> 00:02:12,203 So, in this example, we use the asynchronous 48 00:02:12,203 --> 00:02:16,390 readFile function, which accepts a callback function. 49 00:02:16,390 --> 00:02:19,120 This will start reading the file in the background 50 00:02:19,120 --> 00:02:22,360 and then, immediately move on to the next statement, 51 00:02:22,360 --> 00:02:25,830 printing to the console the string-reading file. 52 00:02:25,830 --> 00:02:30,530 So, again, you see, we are not blocking the execution here. 53 00:02:30,530 --> 00:02:33,860 Then, when the file is finally completely read, 54 00:02:33,860 --> 00:02:35,870 the callback function will be called, 55 00:02:35,870 --> 00:02:38,100 and so, the data that was read 56 00:02:38,100 --> 00:02:40,270 will then be printed to the console. 57 00:02:40,270 --> 00:02:41,890 So that's quite different 58 00:02:41,890 --> 00:02:43,893 from the synchronous version, isn't it? 59 00:02:44,870 --> 00:02:46,710 Now, the question here is, 60 00:02:46,710 --> 00:02:49,490 why does is actually have to be this way? 61 00:02:49,490 --> 00:02:53,940 What's the problem with blocking code execution in Node.js? 62 00:02:53,940 --> 00:02:57,030 Or, in other words, why do we actually use callback 63 00:02:57,030 --> 00:02:59,770 so many times in Node.js? 64 00:02:59,770 --> 00:03:01,523 Well, let's find out. 65 00:03:03,110 --> 00:03:05,930 And in order to understand these questions, 66 00:03:05,930 --> 00:03:08,220 the first thing that we need to understand 67 00:03:08,220 --> 00:03:11,260 is the fact that a Node.js process, 68 00:03:11,260 --> 00:03:13,760 which is where our application is running, 69 00:03:13,760 --> 00:03:16,410 there's only one single thread. 70 00:03:16,410 --> 00:03:19,720 And the thread is just like a set of instructions 71 00:03:19,720 --> 00:03:22,200 that is run in the computer's CPU. 72 00:03:22,200 --> 00:03:25,200 So basically, the thread is where our code 73 00:03:25,200 --> 00:03:29,270 is actually executed in a machine's processor. 74 00:03:29,270 --> 00:03:33,120 So, remember, Node.js is basically single-threaded 75 00:03:33,120 --> 00:03:36,980 and so, for each application, there's only one thread. 76 00:03:36,980 --> 00:03:40,300 That's just the way Node.js was designed. 77 00:03:40,300 --> 00:03:43,050 Now, what that means is that all the users 78 00:03:43,050 --> 00:03:46,960 accessing your application are all using the same thread, 79 00:03:46,960 --> 00:03:50,040 so, basically, accessing the same thread. 80 00:03:50,040 --> 00:03:53,410 And so, whenever they're interacting with the application, 81 00:03:53,410 --> 00:03:55,860 the code that is run for each user 82 00:03:55,860 --> 00:03:59,810 will be executed all in the same thread at the same place 83 00:03:59,810 --> 00:04:02,490 in the computer running the application. 84 00:04:02,490 --> 00:04:04,900 And that is true no matter if you have 85 00:04:04,900 --> 00:04:09,900 five users, like in this diagram, or 5,000 or 5 million. 86 00:04:10,610 --> 00:04:12,080 All right? 87 00:04:12,080 --> 00:04:15,310 Now, what this also means is that when one user 88 00:04:15,310 --> 00:04:17,960 locks the single thread with synchronous code, 89 00:04:17,960 --> 00:04:19,640 like we just saw before, 90 00:04:19,640 --> 00:04:22,280 then all other users will have to wait 91 00:04:22,280 --> 00:04:24,680 for that execution to finish. 92 00:04:24,680 --> 00:04:27,010 And that might not be a huge problem 93 00:04:27,010 --> 00:04:29,800 if you have like five users, 94 00:04:29,800 --> 00:04:33,350 but it definitely will for thousands or even millions 95 00:04:33,350 --> 00:04:35,393 of users at the same time. 96 00:04:36,440 --> 00:04:39,830 So, imagine there's a user accessing your application 97 00:04:39,830 --> 00:04:43,280 and there's a huge synchronous file read in your code 98 00:04:43,280 --> 00:04:46,630 that will take like one second to load. 99 00:04:46,630 --> 00:04:49,920 This will mean, of course, that for that one second, 100 00:04:49,920 --> 00:04:52,370 all other users will have to wait 101 00:04:52,370 --> 00:04:57,370 because the entire execution is blocked for that one second. 102 00:04:57,490 --> 00:05:00,680 So if those other users want to do some simple tasks, 103 00:05:00,680 --> 00:05:02,940 like logging into your application 104 00:05:02,940 --> 00:05:06,900 or just requesting some data, they won't be able to do so. 105 00:05:06,900 --> 00:05:11,150 They will have to wait until the file is finished reading. 106 00:05:11,150 --> 00:05:15,130 Only when that happens they will finally be able to perform 107 00:05:15,130 --> 00:05:18,113 the simpler tasks, one after another. 108 00:05:19,260 --> 00:05:23,290 Now, please note, that this is a very oversimplified version 109 00:05:23,290 --> 00:05:27,010 of what really happens behind the scenes of Node.js, 110 00:05:27,010 --> 00:05:29,880 which is why we will come back to all of this 111 00:05:29,880 --> 00:05:33,760 in the next section and get an even deeper understanding 112 00:05:33,760 --> 00:05:38,090 of how Node.js handles asynchronous code under the hood. 113 00:05:38,090 --> 00:05:39,370 But at this point, 114 00:05:39,370 --> 00:05:42,170 this is enough for you to understand the concept. 115 00:05:42,170 --> 00:05:44,560 It's better to go step-by-step here 116 00:05:44,560 --> 00:05:46,520 and not make it too confusing 117 00:05:46,520 --> 00:05:49,220 right from the beginning, okay? 118 00:05:49,220 --> 00:05:51,660 Anyway, this is how the situation 119 00:05:51,660 --> 00:05:54,620 would play out with synchronous blocking code, 120 00:05:54,620 --> 00:05:58,460 which is obviously a terrible experience for your users. 121 00:05:58,460 --> 00:06:01,180 And so, it's really your job as a developer 122 00:06:01,180 --> 00:06:03,260 to avoid these kinds of situations 123 00:06:03,260 --> 00:06:05,113 by using asynchronous code. 124 00:06:07,150 --> 00:06:10,180 So, for the same situation, we should, of course, 125 00:06:10,180 --> 00:06:12,780 use the asynchronous file read function, 126 00:06:12,780 --> 00:06:15,190 which instead of blocking the single thread, 127 00:06:15,190 --> 00:06:17,700 does the heavy work in the background, 128 00:06:17,700 --> 00:06:20,170 where it basically stays until it's finished 129 00:06:20,170 --> 00:06:22,700 reading the data from the file. 130 00:06:22,700 --> 00:06:25,950 Of course, we then also register a callback function 131 00:06:25,950 --> 00:06:29,490 to be called once the data is available. 132 00:06:29,490 --> 00:06:32,130 And in this scenario, all the other users 133 00:06:32,130 --> 00:06:35,100 can then perform their tasks in a single thread, 134 00:06:35,100 --> 00:06:38,710 one after another, while the file is still being read 135 00:06:38,710 --> 00:06:40,390 in the background. 136 00:06:40,390 --> 00:06:43,870 Now, once the data is read, our callback function will, 137 00:06:43,870 --> 00:06:46,240 of course, get called to be executed 138 00:06:46,240 --> 00:06:51,240 in the main single thread in order to process the read data. 139 00:06:51,380 --> 00:06:52,460 And that's it. 140 00:06:52,460 --> 00:06:54,720 That's an overview of how Node.js 141 00:06:54,720 --> 00:06:58,000 handles asynchronous behavior in order to implement 142 00:06:58,000 --> 00:07:00,850 the non-blocking I/O model that we talked about 143 00:07:00,850 --> 00:07:03,670 in the intro lecture, all right? 144 00:07:03,670 --> 00:07:07,240 And I/O simply stands for input-output, 145 00:07:07,240 --> 00:07:10,810 which is basically stuff like accessing the file system 146 00:07:10,810 --> 00:07:13,500 and handling network requests. 147 00:07:13,500 --> 00:07:16,470 This is actually the whole reason why Node.js 148 00:07:16,470 --> 00:07:18,830 is completely designed around callbacks, 149 00:07:18,830 --> 00:07:21,190 as you will see throughout the course. 150 00:07:21,190 --> 00:07:24,090 In other programming languages, like PHP, 151 00:07:24,090 --> 00:07:27,260 it works very differently because you get, basically, 152 00:07:27,260 --> 00:07:29,640 one new thread for each new user, 153 00:07:29,640 --> 00:07:32,020 which is a completely different paradigm 154 00:07:32,020 --> 00:07:34,600 and really works completely different. 155 00:07:34,600 --> 00:07:37,620 But the creator of Node.js found this model 156 00:07:37,620 --> 00:07:40,660 to be the best solution for building highly performant 157 00:07:40,660 --> 00:07:42,980 and scalable web applications. 158 00:07:42,980 --> 00:07:46,810 Now, just as a final note here, it's important to know that, 159 00:07:46,810 --> 00:07:48,830 when we use callbacks in our code, 160 00:07:48,830 --> 00:07:53,380 that doesn't automatically make it asynchronous, all right? 161 00:07:53,380 --> 00:07:56,520 So, passing functions around into other functions 162 00:07:56,520 --> 00:07:58,780 is quite common in JavaScript, 163 00:07:58,780 --> 00:08:01,830 but of course, again, that doesn't make them 164 00:08:01,830 --> 00:08:05,110 asynchronous automatically, okay? 165 00:08:05,110 --> 00:08:09,150 It only works this way for some functions in the Node API, 166 00:08:09,150 --> 00:08:11,210 such as the readFile function 167 00:08:11,210 --> 00:08:14,823 and many, many more, as people explore in the future. 168 00:08:16,610 --> 00:08:18,500 And now, just to finish, 169 00:08:18,500 --> 00:08:21,200 since we're talking about asynchronous code here, 170 00:08:21,200 --> 00:08:24,630 just one last note about callback functions. 171 00:08:24,630 --> 00:08:27,670 So, this callback model that we just discussed, 172 00:08:27,670 --> 00:08:29,370 where one function is called 173 00:08:29,370 --> 00:08:32,300 once the one before has finished it's work, 174 00:08:32,300 --> 00:08:36,970 can quickly lead to some hard to read and unmanageable code. 175 00:08:36,970 --> 00:08:39,830 Just take this example where the second file read 176 00:08:39,830 --> 00:08:41,870 depends on the first one, 177 00:08:41,870 --> 00:08:44,800 then, the third file read depends on the second one, 178 00:08:44,800 --> 00:08:47,560 and finally, we want to use the final data 179 00:08:47,560 --> 00:08:49,700 to write a file as a result. 180 00:08:49,700 --> 00:08:52,690 That looks quite confusing, right? 181 00:08:52,690 --> 00:08:54,950 I mean, it's gonna work just fine, 182 00:08:54,950 --> 00:08:57,330 but it's just hard to reason about 183 00:08:57,330 --> 00:09:00,110 and that is just with four levels deep. 184 00:09:00,110 --> 00:09:02,980 Imagine you had like 10 or 20 levels, 185 00:09:02,980 --> 00:09:05,850 which is actually not that uncommon. 186 00:09:05,850 --> 00:09:09,440 Anyway, this is what we call the callback hell. 187 00:09:09,440 --> 00:09:11,370 It's such a common problem, 188 00:09:11,370 --> 00:09:13,780 that it already got its own name. 189 00:09:13,780 --> 00:09:16,920 And do you notice this triangular shape here? 190 00:09:16,920 --> 00:09:20,840 That's a very clear sign that you're in callback hell. 191 00:09:20,840 --> 00:09:24,350 Now, how do we actually escape callback hell? 192 00:09:24,350 --> 00:09:27,600 Well, we can use more advanced tools for handling 193 00:09:27,600 --> 00:09:30,730 asynchronous code, like ES6 promises 194 00:09:30,730 --> 00:09:34,150 or even better, ES8 async/await. 195 00:09:34,150 --> 00:09:36,320 Now, the model that we just talked about 196 00:09:36,320 --> 00:09:37,890 will still be the same. 197 00:09:37,890 --> 00:09:39,960 We just have more elegant ways 198 00:09:39,960 --> 00:09:43,370 of dealing with the code itself and writing it. 199 00:09:43,370 --> 00:09:45,830 And there is a whole optional section 200 00:09:45,830 --> 00:09:50,090 of it later in the course on promises and also, async/await, 201 00:09:50,090 --> 00:09:52,590 so in case you're not familiar with them. 202 00:09:52,590 --> 00:09:55,140 But for now, we will keep using callbacks 203 00:09:55,140 --> 00:09:57,900 because that is what Node originally used 204 00:09:57,900 --> 00:10:00,100 and was designed around. 205 00:10:00,100 --> 00:10:02,030 And now, with that being said, 206 00:10:02,030 --> 00:10:05,240 let's move on and use this asynchronous model 207 00:10:05,240 --> 00:10:07,233 in practice for the first time.