So, I think we can start now. Again, my name is Guillermo. I'm your host today. Thanks for joining this workshop. And a special thanks for coming because I know this is not the flashiest topic out there. Like basic principles and
patterns is not like a fancy framework that everybody's using or things like that. So, yeah, good for you too, I guess. Yeah, there's going to be a recording of this session and I think that the organization will put it somewhere you can all access it. Yeah, there you go. Cool. So, again, my name is Guillermo. I'm a software developer, probably like most of you. I work at the company called The Unsimple as a staff software engineer. I normally work on Ruby, but I also work a lot on
javascript and in the past, I've also worked on Java code bases too. At The Unsimple, we are a small team. And by the way, we are looking for people to join us. So, be sure to check out our jobs page. If you're on the market for a job, we'll be happy to hear about you. So, this workshop is titled Back to the Basics, right? And I really meant it when I put that title because when I give talks and trainings like this one, I always try to bring it down to basic principles and
patterns because that's what I honestly believe is the best investment anyone can make in their own formation because
frameworks and libraries tend to change so much, they are really volatile, whereas basic principles and
patterns are things that have been around for a long, long time and have been proven to be valuable and useful through time. So, there you go. That's basically how I try to think about it. Also, in a two-hour remote workshop like this one, there's not a lot of room to talk about a lot of things and go into a lot of detail. So, that's why I wanted to focus just on two topics, one, objects, and two, asynchronous behavior of
node.js because I think those are topics that most of the people I know struggle with and these are also things that we take for granted without really having too much time to think about them and reason about them and get really into knowing how they work. So, let's cut to the chase. There's someone asking if practical notes are going to be given. No, I'm sorry. I haven't prepared any practical notes for you. It's just about dealing with the code that we are going to see for the next two hours and, of course, the repo will be there. It's public and you can do whatever you want with it afterwards. So, again, these are nuanced topics that require a lot of time to get into details, et cetera, and what I'm going to say then during the next couple of hours, you need to understand that these are only mental models, heuristics, ways to think about things, not canonical descriptions of how things are in the language. Okay. So, let's start with the objects. So, the first topic was objects are for Mars. Functions are for Venus. This was kind of a silly joke. But really, what is an object and why should I care? That's just a question I will try to answer. And the thing is that the way I think about objects is as if they were values that have the shape of a collection of key value pairs. So, in
javascript, keys of those objects are called properties. And values, what is a value, right? So, okay. I'm going to enable this. I don't know if it's going to work. So, live transcription. There you go. It works. Okay. Let's continue. So, what is a value, right? Whatever you can put to the object, whatever you can put to the object, it's going to work. Whatever you can put to the right of an equals sign, that's a value in
javascript. And you will find that almost anything is a value. You can actually put anything you want to the right of an equals sign. I was really trying really hard to think of something that was not a value, but I didn't find anything. So, let's jump to the first example I have for you. We have a couple of folders. One is objects and the other one is called a sync. So, let's take the part1.js file and you will find three lines here. And this is showcasing how an object can be thought of as a collection of key value pairs, right? So, in the first line here, we are creating a variable called foo. And to the right of the equals sign, I mean this part of the equals sign, we have a curly brace and then an a, which is the property's name, then a colon, and then 44, which is the value I'm assigning to the property called a. So, this is an object right there. And yeah, it has only one property with just a value. And I know that we are kind of talking about very simple things, but this, we will grow on top of everything we introduce, right? So, we need to establish the basics right here. This is what we are doing right now. The way that we can reference the a property on foo is by using the dot notation or the square brackets notation. And you have an example of that in the following lines. The first line here, console.log foo.a, this is a dot notation. And the next line is using the square brackets notation where you have foo square bracket, oops, I'm moving that square bracket, and then the property name as a string, and that will get you 44 in both cases. So, the way the code sandbox works is that you have to the bottom right, you have a section with a console tab and a problems tab. And you have a very small plus sign on the right edge that you can click. And that should get you a new terminal. And I'm going to enable that for all of you that are following me here. So, if you create a terminal, this is basically a prompt where you can run things. And I'm not sure if you are seeing this, but I'm writing node objects slash slash part one. Oh, I have my keyboard in Spanish. So, let's change that. Objects part one dot js. And then I hit return. And there you go. Oh, by the way, I gave you edit permissions to change stuff. So, please try to not change things. If you accidentally change something, just give me a heads up in the chat and I will restore it. But we are basically sharing the same files. So, please don't change things. This is because otherwise you won't be able to create the terminal to the right. So, again, if you open a terminal, and we are going to use this a lot during the workshop, you can run this file that we are editing here, the part one dot js file. So, can anyone confirm that they were able to open a terminal and run this in their cold sandbox session, please, for me? Okay. A couple of conference more, please, so that I can move on. I created a terminal, but actually I'm not able to write any code. So, I cannot confirm. But I see your screen. So, for me, good start. And maybe later I will fix it. Okay. During some rehearsals, some of the people that tried this had the same problem, and they kind of refreshed the screen and it started working for them. So, maybe you can try that too. But yeah, if you are following me on the screen and you are seeing what I did here, you will see that I run the script, and then I got the two console log lines from lines three and four. And I got what we expected, right? We got full dot A equals 44 and full square bracket of A equals 44. Okay. So, refreshing works. Awesome. This is working. I wasn't sure that maybe with 60 people, I don't know, it would stop working or something. But yeah, I'm happy to see that you'll... Yeah, and I confirm that refresh is fixing and I was able to create again the terminal. It's working. Thank you. Thank you. Awesome. This works. Okay. Cool. So, let's continue, right? I'm going to make this bigger for us. Cool. So, we've seen that objects are collections of key value pairs, keys are called properties, and almost anything can be a value. So, next, what I'm going to do is I'm going to remove this comment here so that now the file will execute until line 21 in this case. Cool. So, what we're seeing here is that functions are also values too. Like, they can be assigned as values of a property. So, to show that, I'm creating here a full function that will create some constant and do some console log call here. And then I'm creating a par object that has a p property. And then this par object also has a foo property. And its value is the function that I created on line 8. Do you see that? That it's referencing this function here. And now, what I'm doing in lines 18 and 19 is basically calling what is the same function in two different ways. And this is important that we understand how different is calling the function in isolation like I'm doing here. I'm just, you know, taking the name of the function and using some parenthesis here to execute that function. This is different than doing this where I run the foo function as a property of the var object, what it's being held or stored in the foo property. Right? So, let's run this in the terminal. Node objects slash part two. Oh, no, no, part one, sorry. And then what we got, I'm going to copy this in the chart. So, what we got is the first two lines from the previous experiment and then two new lines. And here we need to take a look to what the console log is doing here. The console log will print some value called A, which we are defining in line 6. Then it's printing out this dot B, whatever that is. We haven't explained what is this really, but it's doing that for now. And then it's printing out C, which is declaring the same function. Right? So, the value of A and C, I think it's easy to, it's probably easy for you to link the, you know, that this is going to be one and this is going to be C. It's probably easy to see. It's more difficult to see what is going to be this B right here. So, the first console log that corresponds to line 18 is printing out undefined. And the second console log corresponding to line 19 is printing out 33. And this is because this, for some reason, when I call the function as a property of the bar object, it's understanding that this, like this is actually the bar object. So, when it's referencing the B property on this, it's going to take take this one here, B equals 33. On the other hand, when I run the function in isolation, the function doesn't know about any of these dot B. And that's why I'm getting undefined. So, the bottom line here is that functions have what we call a context of execution. And that context is basically the value of this, which is a special name. When you call this in a function, it can mean different stuff, different things, depending on the context of execution of that function. When you call the function in isolation, like I did here, this will correspond to the global object. And when you call the function as a property of an object, like I did here, this will become the object itself. And we can prove that by declaring here value for B. And now that I saved, if I run the script again, oh, it's, well, never mind. Don't worry. Let's run the next example to see the context setting here. Also, another form of setting the context, bind bar, there you go. Yeah, there are different ways to set the context using the using the different methods in the function objects. I'm using only call and we'll see that there are others like apply or bin, et cetera. So, let's take a look to these couple of lines here, line 21 and 22. What I'm doing here is I'm, well, first of all, maybe some of you are surprised to see that I can reference some property called call in full function. Maybe you didn't expect that functions are actually objects that have properties as well. That's maybe the first thing that is new to you. And yes, functions are objects in
javascript. So, then they have properties that you can use and methods, et cetera. So, when you use function name.call, basically you are expressing that you want to execute the function. And the call method here has the first argument. And this first argument is what the value for this, basically. You are telling the
javascript engine that this equals an object with B equals 22 in this case. So, if I run this, there you go, we see that this becomes 22 now. And since the full property on bar is also the same function, you can also use .call on it and then do the same thing again. And then you can pass a whole different context to it as well. So, as you can see, methods like call let you set a specific value for this. There are others, like someone in the chat suggested, you can use .bind. I'm going to copy their example. Whoops, I'm not copying anything. There you go. And you can also use full function apply and then something here. And all these things work. And finally, I have this final example here. I hope that you can see what I'm doing here. That I'm calling the function with the same object as context, which is redundant. This is basically, this is exactly the same thing as doing that. So, again, this way to call functions, I don't know how to call it. I don't know if it has a name even. But this way of calling functions when they are part of an object, this is calling the, or executing the function as a method of the object, like a method, like what we commonly understand as an object's method. And the difference is that this will become the object itself. That's the bottom line here. So, and another note about this is that these two ways of executing functions in
javascript are perfectly fine for most of the scenarios you will find. If you find yourself calling call, apply, bind, et cetera, a lot in some project, maybe it would be good to, you know, step back and take a look and, you know, review this in case maybe you are doing things in too complicated way, or maybe you're fighting. It feels almost like you were, as if you were fighting against the language in this case. So, yeah. Something to think about if you are doing this a lot. So, finally, let me check my notes real quick. Awesome. So, as a recap, right? First, objects can be thought of as collections of key value pairs. Keys are called properties. And anything can be a value, really. And then when you use parentheses, like here, after referencing some properties in some object, you are going to execute that function as a method of the object. So, then this becomes something else. This becomes the object. Now that we know about the structure of objects and how to reference properties, the next question we need to be able to answer is how referencing properties works. And to answer that, like, why does
javascript know that A equals 44 when I do this, right? And why does it know about other properties like this one, right? A.toString. I didn't describe a toString property in full. Sorry. This is not A. I intended that to be full, really. So, I'm not describing. I'm not creating a toString property on full. So, why does this work, right? And to answer that, we need to start to look at the prototype. So,
javascript is an object-oriented language that is based on prototypes. It's not based on classes, really. On prototypical inheritance, right? And the way this works is that every object has a prototype chain. And that prototype chain starts at the underscore underscore proto underscore underscore property. This is a special property that all objects have. Almost all objects have. Oh, shit. I'm getting a call. Sorry. So, let's remove all this and run this example here. I'm going to remove all the console logs here to clean up the output. Cool. So, this console.dir method will show us the internal structure of objects like here I'm seeing here. This is an empty object. So, there's nothing really to show about this object. Just a couple of empty curly brackets. But then the next step, like this would be the property on this object that starts the prototype chain. We see this output here. And here we are seeing that this is an object with no prototype that is providing some methods, for example, or some properties. For example, the constructor property, two-string property, value of, etc. And it itself has another property with its next step in the prototype chain. And then the last line here, the prototype of the prototype, is just a null value. So, the first thing to notice here is that all prototype chains will end with the same two steps. The last step is always a null object, or sorry, a null value. And then the previous value, the previous prototype is an empty object. And that's the reason why when I do foo.toString, this is working because toString is not defined in the object itself, is defined in the first step in the prototype chain. And
javascript will try really hard to not return undefined on you. It will search through the whole prototype chain until it reaches null. And then, yeah, it returns an undefined because by now, by this step here, it doesn't find your property, then it's not defined. That's basically how it works. And let's see the chat real quick. Also, yeah, there you go. Cool. So, then the prototype is about a chain that starts in this property here, underscore, underscore, proto, underscore, underscore. And it always ends on a null value, and then the previous step is always an empty object. And that's why we have some properties that we don't have to define every time. They are inherited, and I'm quoting here, they are inherited because some step in the prototype chain has them, and then
javascript will use that instead of just, you know, looking at the object and saying, no, it's undefined. No, no. Step in the prototype chain has that, so we will use that for now. So, this is really, I guess you're all saying or thinking that this is a really complicated way of doing inheritance. Some other languages have this notion of classes that are easier to understand probably, and they don't have things such as underscore, underscore, proto, things like that are really weird. But yeah, I don't know. This is how they designed the language initially. Let's make a small experiment. So, oops. Again, right? If we try to print on screen the A property of the var object, it's going to be undefined, right? Because var here doesn't have an A property, and also we know that the A property is not provided by anyone in the prototype chain for this object. And then what we are doing here in line 17 is we are using foo that we declared up here to be the prototype or the first step in the prototype chain of the var object, right? And then if we do the same thing again, since foo now is providing a value for A, this is going to stop being undefined. We can take a look now. I'm going to save the file. And run the file. And now, as you can see, the first log is undefined. The second log is not undefined. It's 44 because A is provided by someone in the prototype chain. Okay. So, I hope that helps you understand how the prototype chain works, where does it start, how referencing properties works, et cetera, et cetera. So, now let's talk a little bit about classes, right? So, let me tell you that classes don't exist in
javascript. So, I'm sorry, but that's the truth. We have the ability to describe classes because there's syntax for it, but it's only syntax. There you go. Someone told that in the chat. It's syntactic sugar for you. Like, it's to make your life easier. There are also some
security checks in place if you use classes, and that's why we will end up using them. And I will tell you that, please don't do anything else than using the class syntax for this. And now, to understand why we have classes today, first we need to go like a couple of, well, not a couple, maybe more than a couple years back in time, and do stuff like we used to do when there was no class syntax in the language. So, let's consider the second file here, part 2.js. I've jumped to that file. So, here you can see the stuff we had to do before there were classes in the language. Well, there were the class syntax, I mean. You would have to create some function, and the convention here was to use uppercase for the first letter in the function, and this would be called the constructor function, actually. And this function will do stuff with this. It won't return anything. This is a special function, right? Some function that doesn't return anything and does stuff with this and nobody really knew what this was really at this point. And then, the function itself had some prototype property where you would create stuff. Like, grid here is a function that receives an argument and does stuff with it. And then, well, this is probably the most regular, like, normal thing that we are doing. We are basically trying to get a new instance of person calling new on the function with the argument, and then you would use that object as a normal object in any other language. You would call methods, provide arguments, et cetera. And I bet that this is almost normal, but this is, like, what does this mean, right? So, this is how we had to do things, and let's run it real quick to prove that this actually works to you. So, well, never mind this console here. Call here that is just here to showcase, to show you what is being received as this. But then, yeah, in line 12, we call grid, and then we have the console log right there. So, this works, right? But how does this work? So, there you go. So, with these lines, what I'm trying to do is to see the structure of the object and the first step in the prototype chain and then the second step and the third step, okay? So, if I run this, we get the console log from the invocation here on line 10. Then the first console.dir execution is giving us person with name Guillermo. Basically, this is what the language is telling you about this object. That is a person with a name property with value Guillermo. Okay. Then the first step in the chain is an object that has a constructor property that is a function and a grid property that is another function. And then the last couple of steps are the end of the chain with an empty object and the null value as expected. So, what changes here from the object that we've seen so far in previous examples is that we have this step here providing the grid property here. And this is because we executed the person function with uppercase P with a new keyword in front of it. This way of calling functions is intended to get new instances of some class, and I'm air quoting here again. And this new keyword here will change, will completely change the way this function behaves. Like you can see here, for example, that this is not some global object or it's not some object that we know of. We are seeing here that it's some person, some empty person really. It's an empty object. So, new must be creating that object for us and passing it down to the function for us. And then when we print the object on the console, we are getting this person thing in front of the object that shows the properties of it. And this is because
javascript knows that we got this object out of the person constructor function. And basically what this is doing is, well, yeah, let's continue. So, why is this here? And what is this? So, I mean the new step here in the prototype chain. So, basically this is the prototype property of the constructor function. This is going to become the first step in the prototype chain of this new object. I hope you can see that. This is really complicated. So, it's completely normal to be lost already. But I hope that you can see now that this is going to be true. Like what I wrote here, the underscore underscore proto property, like the start of the prototype chain in the new object is the same thing as the prototype property of the function of the constructor function. And if I run this with the console.log, I hope to get a true value here. Let's see. There you go. True. So, this is the way it works. You have to create some function. Then the function would be used to set the state of the new object, like set the name here in this case. Then you would create stuff in the function's prototype property. And then you would get new instances using the new keyword. And the deal is that you will get a new object and that object will have the prototype property of the constructor function as the first step in the prototype chain of that object. And that's why basically you can call Guille.grid, Javier, even though Guille doesn't have the property grid. The property called grid is provided by the prototype of the constructor function. Okay. This is really confusing. Don't worry too much if you are a little bit lost at this point. It's perfectly normal. The bottom line is that I'm showing you this to sort of justify why we have the class syntax and hopefully to now be able to understand that a little bit easier. The reason why we don't use this anymore, or the reason why they introduced the class syntax into the language is because you could do all sorts of stuff that would be dangerous. For example, of stuff that would be dangerous for you. For example, you could call the constructor itself referencing it from the object. And this would change the object itself, which is really confusing. Now the EJ object has a bar name, and this is really unexpected because you are calling some constructor. Then maybe you would expect to get any object or anything like that. This doesn't make sense at all. Dangerous stuff to do. Another thing you could do is take the constructor and call it with something weird like this one, like in this instance, and then expect this object to be something. This doesn't work, of course. Javier is going to be undefined because the constructor function is not returning anything. It doesn't use the return keyword. So anything you try to assign, this is not going to work the way you expect it. The most dangerous thing you could do with a constructor function is to call it without using the new keyword here. I'm not using new. This is really dangerous, and this was one of the main pains that this would create. This is dangerous because if you don't use new, then the language will assign this to be the global object. Here you can see that this is the global object, like you can see here. Then what this means is that this is going to be run, and we are basically adding some name property on the global object. It's called polluting the global context, and this is something that everybody agrees that is not good to do. Then you won't get anything again because the person doesn't return anything, and then Javier will be undefined, like you can see here, and then the name property on the global context will be set to something. So imagine that you are doing this a lot, and then a couple of constructors use the same property name. So the last one that you execute is going to overwrite the value and all sorts of wrong behaviors you can expect, and this is really hard to
debug, too. So yeah, everybody agreed that having to go through all these scopes just to get one object was a bad idea about designing the language, so they introduced class syntax. Exactly, yeah. Someone is also mentioning the use strict directive or keyword. I don't know how to call it, really, but I bet you've seen this somewhere already. This is something you put at the start of the script, and then the language will prevent you from doing crazy stuff like this. It will actually complain, but you need it to not forget about doing this. So to make things much, much easier for everyone, the language introduced the class syntax, and I hope we can all agree to that this is much easier and much nicer for to get the same result, right? First of all, we are using syntax that is very similar to other languages. You have the class keyword here. There's some special method. In this case, it's called constructor. In Ruby, it's called initialize. In Java, it has the same name as the class. Different languages have different conventions, but we can all agree that it's easy to spot. This is the constructor that we are going to use, and then the syntax to create new methods is nice too, and there are no strange prototype properties. Yeah, nothing really. Everything is looking fine so far, or I hope it is. Then the usage part is exactly the same. You use new on the class name, and you just use the methods here. The result is also the same. If I run this file now, it's part three. If I run this, I get the same thing. I get a person with a name property with Guillermo. I get a first step of the prototype chain that looks very similar than the one that I got with the prototype chain. Then the last two steps are the null value and the empty object. Everything is looking fine. It's not exactly the same. If we went in to analyze this with a debugging session, et cetera, we will see different stuff, but overall, it's the same concept apply. Here you can see also that classes are really syntactic because under the hood, what you have is a prototype chain just like the same you had before. The best part of this is that you get a lot of safety out of it. If you try to use the constructor in isolation, well, not in isolation, invoke it as a method, the language will complain. It will tell you that person cannot be involved without a neo. If you try to run it in isolation without a call, it's going to complain too. Same stuff. You need to invoke it with neo. If you try to call the constructor function without neo, it's going to complain too. So much, much safer to use this syntax. That's why in general I would tell you please don't ever try to do anything like this unless you are super like you know what you're doing basically because it's going to be bad for you in the long run. Okay. There you go. Cool. So someone is asking, what is the difference between invoked and called in JS? I think it's a synonym you can use like when talking about code, invoking a function, calling a function, executing a function. It's all the same. Maybe the verb call has a special meaning because there's a call method on functions. Let me show you that. So full function.call. We've seen that, right? You can pass it context. And this has a special meaning because if full function had some, I'm going to remove all this just to make room and be very specific about this. If full function has arguments, ABC, and you are trying to invoke it with call, then you need to provide the arguments as comma separated values. Whereas, for example, if you would just apply, the first argument is the same, but then the arguments of the function itself need to go in array form. Sometimes it's easier to just apply because maybe you are getting the arguments from some other function call and they are already in array form. So doing it like that is more appropriate or more, yeah, you know, just options you have, called and applied. But yeah, invoking, executing, calling, whatever you want. I think it's the same. I hope that answers your question. Okay. So again, let's go to part three, part four. Okay. Again, how to get yourself an object, right? We already used this notation here, right? So this is called object literal. And this is probably one of the most easiest way to get an object. You just open a curly brace, then put the properties, a colon, and then the value. And you can have more of them. And yeah, that works. And you can even, whoops, that's not what I wanted to do. You can even have methods. And now it's probably better if I format it in a way that is easy to read. All right. So this is the object literal notation. One of the easiest ways to get an object. Another one is the using classes, of course. Well, someone is, someone in the chat is mentioning object.create. So we will get there at the end. We can talk about that later. But yeah. Okay. So object literal, using classes to get new objects from it. But then what happens here is that both object literals and classes until
node.js 12, version 12, didn't have the ability of hiding stuff. Like everything was public. Every object, like you as a user of that object would have access to everything that is declared in that object. And I don't know if you know that, but in object-oriented programming, encapsulation is a big thing. Like you would have, or you should
design your classes and objects in a way that encapsulates the internal implementation details of that object and only exposes a public
api or a public set of
data that users should use and not the internals. Like that's basically encapsulation. And in
javascript, before
node.js 12, you were using classes or you were using object literals, you wouldn't be able to do that. You would be forced to
design your assets, making everything public and maybe probably having some conventions about putting underscore in front of a property name, maybe to indicate that you shouldn't use that or whatever. But there were no hard rules about this. And this was especially problematic when dealing with objects coming from
frameworks and libraries. But after
node.js 12, it turns out that you can actually have private staffing classes. And the way this works is by using the, well, this is called the ash mark. For some reason, they call this ash mark. So let's roll with it. So you declare the name property to be private by prefixing the ash mark. And then what you get is you will be able to reference that property with the ash mark, whatever inside the class, but not outside the class. The language will prevent you from accessing that. So let's run this first to see that it works. There you go. And right off the bat, you can see that when I console lock the object, I'm not seeing anything about the name property. And being
javascript, the language it is, where you can basically do whatever you want in memory, like in runtime, you could think, okay, I know that name exists. I'm just going to add a method on the object dynamically. Dynamically, that returns it, and that's it. And this should work, right? Well, I have bad news for you. This is not going to happen. And this actually is a pretty good safety standard in place, I think. Because it's going to tell you that name is private and you need to declare this method in the object. Basically, what this is telling you is that you need to have access to the source code and you have to be able to deploy the source code in wherever this is running. So this is a high
security standard, in my opinion. It's not like this is a maturity sign to me. Like, the language is getting really mature in some way. So yeah, starting from with
node.js 12, you can do this. So yay, this is good news. Before
node.js 12, though, how would you deal with this? Sometimes, you couldn't deal with this because you actually needed a class. Maybe this was a model that was going to be instantiated a lot of times. So you really needed a class to make the whole process lightweight. But sometimes, you were designing some service that you are going to instantiate only once. So when that was the case, and we are moving to the part five file, you would use the module pattern. And the module pattern in Node, in
javascript, it's just about using functions that return something. And what you get from doing this is every time you open the curly bracket here to start implementing this function, you are getting a local scope where you can declare functions and variables. And none of that will escape that local scope, meaning that outside the function, whatever you create inside that function is going to stay hidden away. Like nobody is out of reach, right? Sorry. So by following this structure here, what I'm doing here is I'm creating a function called gritter with capital G that returns another function. It's anonymous, doesn't have a name. And the gritter function receives a name. And the function that I returned takes other name and prints out something in the console. So the deal is that once I call the function with capital G, I get the function. And at this point, the name argument, like this value here, is out of reach. Nobody can, you know, by through manipulation of the object, nobody can reach out that value. I'm hiding that. I'm encapsulating that. I'm making that private. And I'm air quoting here. And that's basically the way to deal with that. I'm using functions and the local variables, variable scope of functions in order to hide away stuff and only return or publish or, yeah, make public whatever I'm returning. And if I run this and take a look to the prototypes, et cetera, what I'm going to see is, well, this is the line that I get from calling the function itself. But then, yeah, the last two steps are what we expect, the null value and the empty object. And then there's a third step, a special step here. And this is the function instance. Sorry. Basically, the language will identify this gritter variable as being a function by putting this in the prototype chain. And by the way, this is also what is providing me with apply, bind, and call methods that we've already talked about. Cool. So maybe this is like, it's hard to see the value of this construction with such a simple module here. But if we hop into the next file, part 6.js, you will see that this is actually something that I did all the time. Whenever I had to
design a service, I would do it like this. I would create a module for the service. I would create stuff here. And then at the bottom of the module, I would return an object. And that object is the
api of the service. And here, what I'm doing is I'm creating three functions. And I'm returning only two of them, create and say goodbye. Well, I'm returning an object that has a property that has a value of a function that is declaring this in the local scope of the gritter2 module. And the object that I'm returning here is pulling with it the definition of the log function. Because the object depends on grid. And grid depends on log. And log doesn't depend on anything. So this works because the object that you return includes the closure that pulls all the values it needs to work. And you could here do anything you want, do anything you want, like create const a 42. And then use it here. And nobody is going to have access to a unless you explicitly publish it through the object that you're returning. I hope that this makes sense. Like the way to make it public is to include it as a return value. So basically, in some sense, this is a way to implement objects or describe objects in a way that everything is private by default. And then you choose specifically what you want to make public. Cool. So if I run this file now, let me clear the screen. What we see is that now the prototype chain has the, well, as always, the null value goes last. Before that, it's the empty object. And now we have the gritter object has a grid with a property and a say goodbye property, as we expect. And yeah, just a couple of notes about the module pattern here. You have a couple of examples in part seven.js. The first one here is called the self-executing function. I bet that most of you have seen some form of this somewhere This is a way to declare a module and execute it without polluting the global context because this function doesn't have a name. It doesn't need a name because we are executing it with this parenthesis right after declaring it. So it doesn't need a name. Therefore, it doesn't need to be stored in the global context in any way. It will just declare it and run it, both things in sequence, and there's no pollution of the global context. This is also the way that most code transpilation and
packaging in software works like
webpack and others will take your
typescript sources or whatever language you're using these days. They will take that, transpile it, and put it, wrap it with self-executing functions so that when it's delivered in a browser, this is executed, and there's no side effects on the global context either. The second example here is what
node.js actually uses to wrap around your .js files. This is the module wrapper. You can see here that it will receive the exports, require, module, and other arguments that are available in your files. They are being provided by
node.js when it wraps around your file with this self-executing function. With that, we have hit all the contents I wanted to go through about the objects. We have still a question to address, and before we do that, why I think this is useful. For me, this is useful especially when troubleshooting issues in systems, and especially when objects are coming from libraries and
frameworks because when you are with the final end-user system, your
design tends to be more flat probably, and the type hierarchies are flat there, so there's not much going on in the prototype chain, but when you are dealing with objects that come from
frameworks and libraries,
frameworks and libraries are designed to be reusable and abstract. That is achieved in many ways, and some of them have to do with complex type hierarchies that require complex prototype chains. When you get one of those objects, it's really useful to know your way around prototype chains and things like that and knowing how instantiation works because when there's an issue with those objects, you can go in and get to the answer quicker than if you didn't know that, of course. A second way that I think this is useful is because now I can ask myself when designing a new system, am I going to need a lot of instances of the same thing? Then I would probably have to
design a class because having a prototype chain is a faster way of making sure that all objects have the same methods and properties. Maybe I'm going to have only one instance of it, it's a singleton service or something like that, then I can probably get away with just a module. Everybody can think differently here, but I think differently here, but I think that this module here, since it doesn't have to do with prototypes or classes or inheritance or you don't need to use new at all, this is just functions and variable scopes, to me that brings less complexity to the solution, so it goes more in the style that I like then maybe I will use this instead of using a class that only I instantiate one object out of it. But now I have options and that's what I value really. Now would be a good time if you have any questions, you can ask it in the chat on Zoom and I have one question from someone that is have you seriously used object.create with a non-node prototype in production? That's a really good question. In my opinion it's a very
advanced topic to talk about probably, but let's try it. This person is referring to this thing you can do object.create. Oops. That's not what I meant. And here if I'm not mistaken you can pass the prototype here, right? Like if I pass null here and I assign that in a variable like that and then I do console dir a and then I try to do that and I run it. There you go. So a is an object with null prototype and the prototype property of that object is undefined. So basically what we are getting by doing object.create with null is an object that doesn't have a prototype chain. Some people think that this way of creating objects is faster, it has a smaller memory footprint. This might be relevant when you are dealing with a highly efficient code that needs to go really really fast. I would say that in 99.9999% of the scenarios this is like over engineering it. Why don't you just, what is the problem of doing that? Right. But another side effect of going this path is that now a to string is nothing and this will fail. There you go. A to string is not a function, right? Right. Yeah. So some people is answering because you are not dragging the default properties or I would use it to guard against unsafe input prototype chain. Oh, that's a really good point. Yeah. Well, those are valid points. If that's your scenario, go ahead and use it. These two tend to go together like object.create and object.assign. I encourage you to investigate the research, learn about them. They are there because they are useful in some scenarios. So yeah, I would normally, what I would argue is that the object literal, the class, whoops, instantiation or just, you know, calling a module that gets you some object literal are the main three ways of creating objects that everybody would have to understand and master. And then if your scenario requires more, yeah, why not? Object.create is there for you and object.assign, if needed. They are tools that you can use. But yeah, this is honestly, this is
advanced stuff. Maybe you are designing
video games in
javascript or maybe you are doing
machine learning stuff or I don't know. Maybe your use case creates, requires really lightweight objects. And that's useful. Or maybe for
validation as someone, unsafety as someone suggested. Okay. So first of all, well, my goal for this section is to convince you, if you are not convinced already, of course, that actually
javascript is a really good language to work with non-blocking asynchronous programs. And first we need to establish that this is something that we actually need. Like, why would anyone do that to themselves, right? And it's, like, this is required. Like, we can really work around this. Input output operations are slow. And we can't have the thread blocked while, you know, you are fetching some
data from a third-party server or writing some files down in the file system. You can't have the CPU stopped just waiting for that to end. And this is especially important when dealing with UIs, right? It would be a very bad
user experience if the screen would freeze when some drop-down is loading the items to choose from, right? Nobody does that these days. Like, applications tend to be asynchronous because it's just better. So
javascript solves this. Like, there are multiple ways of solving this problem, right? And
javascript, what
javascript does, it provides you with a thread, a single thread for your program. And then every instruction that needs to be run goes to that queue. And what the engine does is go through that queue until there's nothing else to be run. And then it stops working. So when you are writing a
javascript program, what you are basically telling
javascript is what instructions need to be enqueued and the order in which that needs to happen. Other languages like Java, for example, have a multi-threaded approach. And that approach is probably much more powerful, lets you have more control, and more importantly, lets you use all the resources that you have in the server or the computer that your program is running at. If you have eight cores, you can really, you know, take everything from them, get them to be running at 100% of time occupation and really, you know, extract all the power from the platform you are running your program. In
node.js and
javascript in general, you don't have that option. You only have one thread. If you have ten cores, you will have to use something on top of your
node.js program to extract all that power from the platform. So you have only one thread. And other languages let you access all the threads you can get from the platform. But the trade-off is that they bring a lot of complexity on you. You need to be a really expert. Programmer to
design programs in a way that are easy to understand and easy to maintain. And also another trade-off is that when things break, a multi-threaded program is much harder to
debug. Then, you know, you get into bigger issues probably. So first, like right in the introduction, I'm telling you that the bottom line is probably we should be happy about it.
javascript solves this hard problem to solve in a simple way. I know that it can be overwhelming at first, but I hope that with the things that we are going to see now, you will have the tools to think about it in a more efficient way and simpler way. But yeah, the bottom line is it could be worse. Let's be happy about it.
javascript is awesome. Let's move on. All right. So in
javascript, non-blocking behavior or asynchronous behavior is described through callback functions. And these are functions that are going to be called sometime in the future back. Literally, they mean that, right? And to get the hang of it, let's first play with some piece of code that I have here. Let's go to the async directory on part1.js file. Let's see. Yeah. So we are going to play with the setTimeout function. And the setTimeout function lets you put here a function and some delay. The promise is that this function will be called in no less than 1,000 milliseconds, one second here. Oops. Somebody is changing the directory. Okay. Cool. So again, this code here is going to print A. Then it's calling setTimeout on a function that will print B. And then it will print C. And here the challenge is can you guess the actual order of these logs? I will let you think about this for a minute. You can go and run it in the terminal, too, if you want to see what actually happens. But I would... Let's try to, first of all, before running it, let's try to make a guess. Okay. So someone is already making bets in the chat. ACV for now. P-A-O-K. I don't know what was that. ACV. Cool. Cool. So let's run it. I guess we can actually see it, right? Async. Part one. Cool. So the order is ACV. There you go. ACV. You all were right, I guess. The interesting part here is that there's a small delay between C and B because we can see, right, the setTimeout is setting a delay of one second. So this is expected. AC, one second, then B. Okay. Everything is fine. Nobody made a different guess. So I think we can move on to the second file. And here in this file, what I'm doing is I'm using this module here called sleep that is going to actually stop the thread and prevent anything from running for two seconds. And here, notice that two seconds is more than one second, right? And it's the same sequence. Console log A, setTimeout of console log B, one second of delay, stop the thread for two seconds, and console log C. Again, I'm going to reset here. Make a bet. Like, what's going to be the sequence? Maybe you can also describe how the delays are going to play out in the end result. Can you guess that? Okay. So we have the first guess of ACV. Maybe you can explain. Oh, we have an ABC. This is getting interesting. If you can reason also how the delays are going to play out, that would be great. ABC, ABC. So ABC is winning right now. Okay. So let's cause of notorious life cycle. Okay. That's true. I think, yeah, the person that guessed ACV is right. Let's take a look. ACV. So first of all, the order is the same. Again, you need to think about this as there's one, there's a single thread and there's a queue of instructions. And the queue of instructions is the same with the only difference that before printing out C, we actually have before printing out C, we are sleeping for two seconds, but everything else is the same, right? So the order of locks must be the same, too. What is interesting here is that we are waiting two seconds between A and C and then B is printed out right after, almost instantaneously. So what we see there is that there is no extra seconds delay after we sleep for two seconds. Like this is what setTimeout does. It's letting me describe a delay from that is starting from the last run instruction, well, last enqueued instruction. So since we are sleeping for two seconds, two seconds is more than one, it prints out B instantaneously. It doesn't need to wait more. Okay. Cool. So now let's go to the third example. And here we are not going to block the thread or anything like that, but we are setting the timeout to zero milliseconds. So in your opinion, is this going to change anything? Is the order going to be affected by this? Or, yeah, make your bets, I guess. Okay. So ACB, it won't change, still the same. ACB as well. Yeah, so you're right. Basically, it's not going to change. ACB again, yeah. So you already understand how this queue thing is working. I'm happy about it. So ACB, the order is the same. The fact that we are not waiting for any millisecond at all doesn't change the fact that this is enqueuing the lock first, this is enqueuing the setTimeout the lock first, this is enqueuing the setTimeout call, and this is enqueuing the lock C. And then when this is called, you can teach using StackMod. Yeah, exactly. When this is called, this is enqueued at the last position. And then like, yeah, so the stack by now has A, now the stack has A, then the setTimeout. I'm just improvising here. And then here the stack has setTimeout and then C. And then when this is executed, like the engine starts actually to take instructions and run them, it will take A, and the stack has setTimeout and C. Then it takes the setTimeout and runs it, and the stack has C. And as a result, the stack now has C and B. Because the execution of the setTimeout enqueues this call here. And then finally, C is taken, B is still pending to be run, and then B is taken, the stack is empty, and the program ends. Okay, I hope this improvised. I wasn't planning to explain it this way, but I hope it makes sense. So anyway, yeah, again, the same, I'm just, you know, we are dancing around the fact that there's only one thread and there's a queue of instructions to be run. And setTimeout is really, you know, it has this very misleading name of timeout when in fact it's enqueuing stuff with a time-based trigger, or I don't know. The mental model I follow, the way I think about asynchronous behavior in
javascript in general, has to do more with enqueuing stuff like we did here. And understanding what is going to be the trigger about. In the case of a setTimeout, it's going to enqueue, like it happened here, it's going to enqueue B, but it's B with, you know, with an asterisk, because it's not like the interpreter won't take B out of the stack if not enough time has passed, because maybe I need to wait for seconds, right? If not enough time has passed, B is going to stay there in the stack until that time has elapsed, and then it's going to execute it once that time has passed. And this is exactly the same when we are talking about an Ajax call, for example. It's not a time-based trigger. It's a trigger that is based on some input-output operation that happens in the network layer when some request is done to some server, and then the operating system gets the response and parses it, blah, blah, blah, and then the trigger is like my callback for the Ajax call is going to be that I have the response, and the response has to be parsed, and then the callback is called. And until that response is available, the callback is going to be in the queue waiting for it to be executed. Or, for example, if I'm writing a file in the file system, the trigger will be about that write operation to be completed, right? And then, you know, the mental model, everything is the same. They are just callbacks that are enqueued. They are in the queue waiting to be executed, and they will be executed based on some trigger, and depending if I enqueue them with a set timeout or an Ajax call or a file write, right? Yeah, those are different triggers that we'll be taking into consideration when that callback is the next thing to be run, right? That's the way I think about these things. And this callback thing works really well. It's simple enough when you are dealing with a single callback that needs to be run. When that callback is simple enough, like it's a couple of lines probably that, you know, it's not too complex. But oftentimes, you need to do more complex stuff. Maybe you need to run a huge callback that does a lot of stuff, or maybe you need to coordinate a first call to a service, and then a second call to another service, and then maybe you need to write something down in a file and persist something in the
database and blah, blah, blah. It can get really complicated, right? And when the operations around that are synchronous, it's just about writing your program and that's it. But when everything starts with some non-blocking operation, like an Ajax call or reading a file from the file system, whatever, doing that coordination and breaking up every side effect in a small digestible function that deals just with one thing, that's something difficult to do just, you know, with callbacks. If you're an expert
functional programming software developer, you have a lot of tools to do that. Composition, function composition, you can have monads, you have a lot of tools. And that's perfect. But if you don't have that expertise, or maybe you are working with a team with mixed levels of knowledge, or maybe there are specialists,
functional programming specialists, but there are other specialists that don't have that knowledge and they are more experts, I don't know, in UX or whatever, and they don't know about
functional programming, doing that coordination is really complicated and will make things like collaboration is going to be harder for you overall. So what to do then, right? The bottom line is that you want to use standard
design patterns that are applied to asynchronous programming. And the good news here is that
javascript provides you with a Promise
api that implements a couple of useful
design patterns that let you precisely do this coordination stuff in a more easy way, in an easier way. And we have an example of that in part four. Like this is a really quick introduction to Promises, really. And part four has probably one of the simplest ways of introducing this. Basically, what I'm creating here is a function that is called non-blocking function. It takes some delay and it returns a promise that will resolve once that delay has elapsed. And what you can see here is basically, this is just a wrapper around setTimeout, okay? So, yeah, whenever you call non-blocking function, first of all, you get a promise. Like you can go and assign this into a variable. You can use p1 equals that. And then p1, then blah, blah, blah, okay? First of all, you get a promise. And a promise is an object that represents some asynchronous non-blocking operation that will produce a value, will resolve to a value, or will be rejected with an error. And this function returns the new promise. And the body of the promise is calling setTimeout to call this function in turn. This gets a little bit complicated, but I hope you can all follow. This basically will call resolve with some value once the delay has passed. Cool. So, if we only have one callback, the value of using promises is not so evident. But I hope you can see here that I'm using the then method on the promise. This actually lets me, first of all, like this would be the first asynchronous programming pattern that I want to talk about. It lets me sequence executions of callbacks that will happen, like you are guaranteed that first one is going to be called, then the second, then the third, in a specific order. If I do this, return 33, for example, whatever, if that makes sense, I can then call that makes sense. I can then call with another, oops, with another value. Oops, I can type today. Like that. And I hope you can see that what then does is return another promise. Like that. So, what we are seeing here is that I'm calling, I'm triggering some operation with a non-blocking operation, and I'm describing a sequence of things that need to happen, and thanks to the promise
api, I'm guaranteed that all these calls are going to happen in order, regardless of when this first promise is resolved. And probably one wouldn't write this like that. One would use the chaining of callbacks like this, which, you know, could be nicer for some. But yeah, if I run this, this is part four when the delay happens, and then this is run, and then this is run. And another interesting thing about chaining callbacks in a promise is that the callback that is being executed at every time is getting the value of the previous callback, like the value that the previous callback is returning. This way, what you are defining here is almost this functional pipeline of functions that, start with some value and pass down, do something with that value and pass down a new value to the next step in the chain. Therefore, when you call non-blocking function with a one, the promise is getting resolved with that value, so you get a one here, and then you return something else, and the next step is getting that in turn, and then you can keep returning new stuff down the chain to describe whatever operation you are trying to do here. And again, this is letting you break down the side effects into smaller chunks, and this is letting you describe the order in which these things need to happen. When does delay seconds will play? I don't get this. Yeah, I'm sorry. Yeah, this, maybe the notation is not helping understand things. Sorry. Let's write this slightly differently, okay? Because I'm introducing a function. Function. Whoops. Okay. Is this right? Let's see. There you go. Cool. So, do you remember that we did this here? Set timeout with a function and a delay. So, this is basically the same thing. Oops. Whoops. Where did I go? Sorry. This is the same thing here. The same code. So, the delay seconds by 1,000 is basically letting me say one second, two seconds, and this is what is going to be passed down. Okay. You get it now. So, yeah. Awesome. Cool. Yeah. So, as a recap, I'm introducing here chaining down callbacks. This lets you describe the order, lets you break down the side effects in smaller chunks, and you're guaranteed that this is not going to be called before this is completed. There are other things that this is providing us with is, for example, if I type this like that, I actually can do this. Whoops. Console error. Sorry. So, to keep things simple, I didn't do this before, but every time you call then, you can actually provide two different callbacks. One to be run if the previous promise was resolved, and another one to be run if the previous promise was rejected with an error. And this is often used to do error management, right? And you would do things like log the error like this, or maybe you can return some default value. You know, these kind of default values that you could choose to return in case something goes wrong. And this, what it effectively is doing, like this promise was rejected, right? And then this callback was called. So, by returning something here, what we are effectively doing is here, what we are effectively doing is recovering the flow of the promise into the success path. Like, we went to the error path because something was rejected, but by returning something, we are forcing the flow back to the resolution flow to continue, you know, executing the chain of side effects through the success path. Whatever. Another thing you can do here is to catch. What happens if you provide an error function and also catch at the end? Okay, that's what I'm going to do. So, the catch, catch. What is going on? Okay. So, you, on top of being able to call then, you can also call catch at the end. And this is what is called, well, I'm not sure if this is the precise name, but this is a terminating call. Like, you can't go and then anymore after you call catch. Okay? This is terminating the chain of side effects. And this is letting you do a final error management. Like, if you don't have any error path callbacks, it's not required to provide an error callback. This is optional. So, you could only care about the success path like that. And then, write the catch here at the end. And this will, regardless of what it failed, what it was rejected, this will be executed. What if you have an error callback here and you return 33, for example? Well, if you do that, which I believe the syntax is not right because I'm using return here, it forces me to do that. If you do that, you are going to get the flow back to the success path and catch what we called. And catch what we called. Only if an error is not managed by an error callback, reject callback, then the catch is going to be executed. If the error function doesn't return or reject the thermistor throw, the catch will never be called. There you go. That's the much better explained. Thank you. Cool. So, first,
design pattern around the synchronous operation, sequencing of callbacks. There you go. With the then and the catch and the resolve callbacks and the reject callbacks, you can error manage in a very structured way. Super useful. In part five, in the next file, you have another asynchronous programming
design pattern that is called a semaphore. And this is provided by promise.all. And then you provide an array of promises. Are you telling something about view
engines in
node.js? I prefer to know about AGS. No, I'm not going to touch that. Sorry. This is only about objects and asynchronous programming. I'm not going to talk about any view engine or anything like that. No, no worries. Cool. So, again, this is called a semaphore. You call it by using the all method in the promise object. And you need to provide an array of promises. And what this is going to do, this is going to get you a new promise. P1. And what we are trying to describe here is that P1 is a promise that will be resolved or rejected. Well, resolved, maybe. It's there. When all the promises have settled. When all promises have been resolved. And then and only then, the next side effect will be, the next callback will be called. If we run this example. Whoops. Not the file. There you go. One, two, three. Boom. And you get the callback called. And so notice a couple of things here. First of all, this promise is taking one second to be executed. This one two and this one three. Since I'm waiting for all of them, this one is going to take at least as much time as the slowest of all of them. Right? And the second thing to notice here is that the callback here of P1, the arcs of this callback is going to be an array of values. And each value in that array is going to be the partial value of each one of the promises that compose the semaphore. So it's getting one, two, three in order two. It doesn't like if I did the opposite order. Well, this is formatting the code in a different way. But yeah, I'm setting a different order, right? Three, two, one. If I run this again, I will get three, two, one. It's not about who is fastest or anything like that. It's about guaranteeing that there's some relationship between the way I write the code and the order I get the stuff here as values. This is super useful because it's taking away a lot of uncertainty from an asynchronous operation that is always hard to reason about. Like this is letting you describe things in a very precise way and explicit way. So there you go. This is called the semaphore. You call it with promise.all and it gets you a new promise that will be resolved once every composing promise gets resolved. Therefore, it will be as slow as the slowest of them. Cool. The next
design pattern that I wanted to mention is the race. And this is super useful. I hope you can see this by the end of showing this around. The race lets you literally race a couple or a set of promises between each other. And what this does is basically the callback is going to be called with the value of the first promise that gets resolved or rejected. Since I'm running one, two, three, one is going to win, of course. And then one is executed there. And then the program still needs to wait for the completion of these two calls because these are enqueueing stuff in the queue. Okay. So someone is saying something in the chat. Sorry. But about the catch description above, if the first promise resolution handle throws, not only the first error function, catch will also be called. Yeah. Of course. Yeah. As I said in the beginning, there's a lot of nuances going around. And actually, the promise implementation is deeply complex. There's like, yeah, once I tried to implement a promise engine and it's like the basic stuff you get to understand and it's intuitive, but it can get really complicated really quick. But yeah, there are cases where even if you have a rejection callback, blah, blah, blah, it can get really complicated. I'm sorry, but yeah, there's not much time to go through that. But yeah, going back to the race here, what you see here is that even though the promise is resolved real quick, after one second, the program takes a little bit more to complete. And that's because these two calls here still have to enqueue stuff and those instructions need to be executed. So that means that the program itself will take three seconds waiting for this one, even though the callback was two seconds ago was executed. So that's why we are seeing the delay here after the the one is printed out. And this is a very useful
design pattern because it lets you implement timeouts. And basically, a timeout, what is a timeout, right? A timeout is a race between a promise that will be rejected after some time and your actual logic that you need to run. So if we go and define an async function that is called timeout with a delay seconds, and this is basically the same code that we have here, but instead of calling resolve, we are calling reject after some seconds. And we go and timeout after one second. And I'm sorry about this. This is formatting the code every time I say, but yeah, if I do a race between a timeout that will timeout after one second and an unblocking function that takes two seconds to be run, the timeout is going to win and it gets through the error path. If I invert the times so that it times out after two seconds, but the actual logic takes one second to complete, the logic wins and yeah, we don't get a timeout. So basically this is how timeouts are implemented. And if you're, you know, the tool you're using doesn't let you define timeouts in a nice way, but it works with promises, you can just race for that. You can have timeouts in whatever process that is. And the last
design pattern, and I'm adding this just because it's there, it's available in promise, it's the any method here. And this is basically the same thing. This is a race, but in this case, the any will keep waiting like, whoops, if I have the timeout, again, I copied here and I timeout after one second, then I timeout after two seconds, and then still this is going to be successfully executed, it will wait in case any of the promises gets resolved before, you know, rejecting the whole promise. So if I run that, so if I run that, yeah, in the background, the timeouts are rejecting the promise, but since this worked, we are getting the three. And then, yeah, this is very similar to our race here. And yeah, this is basically the contents I wanted to go through, we are done with the contents and only seven minutes over the two hours max, so I'm really happy about it. Do you have, before we finish the workshop, do you have any questions for me or maybe you can go and copy and paste some piece of code in the chat, we can analyze it together or something like that? Yeah, you're right, Bjorn. Two minutes early, right? Really. Yeah. You're welcome. Thank you all for coming, I guess. I'm really glad that so many people joined. And yeah, 39 still there, so kudos. All right. Yeah, so I guess if, no, thank you all for coming, really. I'm really glad that so many people stayed until the end, you're my heroes. And also, if anybody has any question, anytime, I'm on Twitter and you can reach me on GitHub too, that's my Twitter handle, just drop me a line, I will be very happy to answer or try to help you in any way I can. And again, I work at TN Simple, we are a small team, we are looking for more people to join us, so be sure to check our jobs page if you are on the market. Why did you use the promise syntax instead of a sync away? Oh, that's a really good question, let's answer that. Cool. So async await is this thing that I'm using here. So the deal is that if I use async before a function, I'm forced to return a promise. Like that function, I'm giving hints to the language. Okay, I'll answer that later, Khalil. We have a couple of questions already, this is great. So I am required to return a promise now because I used async. What I get by doing this is that I can, well, this doesn't make much sense, but anyway, I can await for the non-blocking from, for whatever, let's start a new example because this is getting complicated already. Okay, so let's define a function, a synchronous function that is called, well, let's call it foo with some value and delay. And this is going to return a new promise that resolves with a set timeout, returns the value. Like it basically repeats the value that you provide after some delay. Okay. So you can call foo with 42 after 1,000 milliseconds, you get the value. And this should console log 42, right? After one second. Let's try this real quick. There you go. It's working, right? So if, since I used async here, what I can do instead of using this syntax here, I can actually assign the value of foo, of calling foo, of calling foo, if I call foo with an await in front of it, right? And this is not going to work. First, I need to do some tweaks to it. This is going to complain because you can't use await in a function that is not async. Like this is a requirement. So I'm going to wrap everything in a function called run that is async. Okay. And then I'm going to call run for now. It always returns a promise regardless of the return value. The point is await waits for the result, not self-pointing wrapping it when promise always. Oh, yeah. Thank you. I'm going to, yeah, I'm moving towards that. Yeah. The first I need to establish, you know, the base, like how this works, right? You need to wait, and then you can assign any value you want. And then what they are suggesting in the shot here is that I could basically, why not just do something like value one, value two. Oops. I can't type today. Sorry. Value three. And this is 41, 42, 43, for example. Right? And this is basically replacing the promise all, right? Because I'm effectively waiting for all the, for all the promises to be resolved. And if I run this, I should, well, let's have a really quick delay so that we don't have to wait so much. Like this is getting us almost the same thing, right? It's not the same thing, though, because this is running this, waiting for it to be completed, and then running this, waiting for it to be completed, and then running this. And if these are three input-output operations, we are not really letting the underlying operating system deal with them in the best way possible. Like, we are not signalling our program to launch the three operations and waiting for all of them to be completed. Like, this is not, this is not equivalent to the promise all call. This would be equivalent to then dot then dot then dot then in a sequence, right? That's the first difference we have. I don't know if you can wait for more than one thing. Maybe there's a way of doing that, like you would do in a promise all call anyway. And the way to deal with timeouts, I'm not sure, really. I haven't played around with this so much to give you a better explanation at this point, but yeah, I hope that this makes sense. This also is much nicer syntax, I would argue, like using then then then if you can model your program like that, it's really nice. And maybe you don't care about these, all these three operations running at the same time or managed by the operating system. Maybe this is good enough for you. And then this syntax is much simpler, right? Well, that's essentially true. If total returns a promise or return value, the points, yeah, there you go. What do you think about using some kind of application level load balancer like PM2 in production with node since it's single threaded? Do you think it's mandatory to get full benefits of the machine? I don't know. That really depends on your, yeah, I'm sorry, I'm answering, I don't know a lot. It's really about the specific problem you're trying to solve. I would argue that if, for example, you are just supporting a web application, the web engine, maybe it's in Jinx or whatever it is, it's going to spawn a thread for you. And this model of having just one thread and a queue fits really well within that model of execution. If you are doing something else, then you need to explore other options. I haven't, I don't have experience working with multi, like modeling
node.js systems that have a lot of processes, you know, working at the same time and haven't getting, yeah, that's a scenario that I haven't explored. Sorry, I can give you specific answers. And then, Kadir, you also asked, how would you solve when you suddenly need CPU intensive job in node? Well, I don't know. Sorry. I don't know. This is really something that you need to, like, this is
advanced stuff. You need to put a lot of attention in instrumenting your solution really well, being able to analyze your program so that you know that that is actually your problem. Maybe it's another problem you have. And I'm sorry, because you might have this problem and you already might have researched it and you are certain about it. But I see how things can rush into thinking that there is something, some inefficiency in there. Like the root of some problem is some inefficiency and it turns out most of the time that is a simpler problem or maybe something that you can, yeah. Okay. Well, I hope that I answered your question somehow. Is there any other question? Otherwise, I think we can call it a day. Thank you all for coming here again. And I guess I need to stop the recording or something else as the host. So I'm going to do that. Thank you all for coming again. It was really, I was really happy to see so many people staying until the end. And yeah, enjoy the congress, by the way. See you. Thank you. Bye-bye.