Video details

"JavaScript: The Grumpy Parts" with Rob Richardson

JavaScript
11.01.2021
English

No matter how much you love JavaScript, you've got to admit: it's weird. Why does 'this' behave as it does? How does variable scope work? Why do we have such comical behaviour when comparing mixed types? In this talk, Rob Richardson pulls back the covers to explore how JS truly works.
Watch thousands more tech-related talks in our SkillsCast library at https://skillsmatter.com/skillscasts​​

Transcript

Let me share my screen and let's take a look at JavaScript the Grumpy parts. Here's the part where I tell you I am definitely going to post the slides on my site tonight. I've been that person chasing the speaker and it didn't work out very well for me either, which is why you can go to Robert.org and let's do that. Click on presentations here at the top. And here is JavaScript the Grumpy parts. The slides are available there online right now. While you're here on Robert.org, let's click on About Me thanks for the introduction, Jason. I'm a thyroid developer advocate, Microsoft MVP, and a friend of Redgate. Ac Givecamp is really cool. Ac Givecamp brings volunteer developers together with charities to build free software. We start building software Friday after work Sunday afternoon. We deliver that completed software back to the charities. Sleep is optional. Caffeine provided. If you're in Phoenix, come join us for the next AZ Give Camp. Or if you'd like a gift camp in your neighborhood, hit me up on email or on Twitter and let's get a gift camp in your neighborhood too. Some of the other things that I've done, I helped build Gulp in version two and version three as a core contributor. That was a lot of fun. And one of the things I'm particularly proud of, I replied to a Net Rocks podcast episode. They read my comment on the air and they sent me a mug. Woo. So there's my claim to fame. I coveted Net Rocks. Let's dig into JavaScript the Grumpy parts. Not that F twelve. There we go. We talked about this guy a great thanks to John Mills, who created a similar talk, A Guide to JavaScript Scary Side. Now we're definitely going to go in a different direction than he did, but his talk is amazing. And if you have a chance to watch Jonathan Mills, it is definitely a good watch. His Guide to JavaScript, The Scary Side, is available here. As you grab the slides from Robert.org, any of these blue links you can click through to get to the actual website. This is going to be really fun. We're going to dig into some of the innards of JavaScript, and I suspect that we're going to hit the edges of my knowledge. That'll be really fun. I look forward to getting stumped by your questions. I look forward to learning from you too. And so it'll be fun to see how quickly we can get to the answer of I don't know. Let's learn together. So in the beginning, the beginning of JavaScript. This is Brendan Ike. Brendan Ike was tasked with building a Haskell like experience inside of the browser and what resulted was JavaScript. He built it in ten days 25 years ago. If you want to learn more about Brendan Iken, how JavaScript came to be, click on these links from the [email protected] It's pretty cool. 25 years ago, the code that I wrote isn't doing anything anymore. And now billions of us are here working on the project that he created. That's really cool. Ten days ago. Ten days worked 25 years ago. Now, the cool part is as JavaScript has aged and grown, then we have a whole lot of run times that allow us to run JavaScript anywhere. So, for example, we have the V Eight engine that powers Chrome and Node. We have the Spider monkey engine that powers Firefox. Javascript core is the JavaScript engine that powers Safari. And we have Chakra that was in Internet Explorer. Now, the cool part about this is that all of these engines were copied with amazing Fidelity and in particular including the bugs. Now, a lot of times we talk about the differences between JavaScript engines, and with the advent of HTML Five and ECMAScript 2015, we do this less often. But a lot of times when we're comparing the difference in engines, it isn't actually the JavaScript engine that is different, but rather it's interface with the Document Object model with the Dom. That interface with the Dom is very different in different engines. And, well, we're getting more consistent. But the JavaScript runtime itself is amazingly similar across all of these different engines. That's really cool. So we can write once and run anywhere. Now, the interesting thing about JavaScript in the browser was the methodology of how JavaScript came to be is, well, the code is running over there. The developer has left. What we ship down to the browser is our source code. We don't compile it and link it and ship an executable. We ship JavaScript source code. Okay, yes, there are transpilers. Babble and webpack are definitely a thing, but that's the methodology of JavaScript. If the developer has left and the code is running in this memory constrained space. And so the JavaScript engine tries to help whenever it can. If it reaches a syntax error, it'll try to back up and see if it can figure out how to solve this. Now, that means that help. Now, semicolons in JavaScript are one of those pieces where we may choose to leave those off. Javascript, being the helpful runtime will notice that a semicolon is missing. Back up and start to insert one. Now, it may insert a semicolon in places where you don't expect as well. So I always like to leave semicolons in my code to be very explicit where I want this content to be. But that's the methodology of JavaScript that the developer has left, and we want to take the most opportunities we can to continue running in whatever way we can. So JavaScript ends up being weird. And I love this talk JavaScript. If you grab these slides from Robert.org and click Play right here, you'll get to not only the JavaScript laptop, but actually Brendan Ike presenting it at Fluent. Comp. And that's really cool. He stops it from time to time and adds commentary. It is amazing. So, yeah, JavaScript is really weird and just picking out a few interesting examples. Undefined is a variable inside of JavaScript. So what if we defined it in more modern versions of JavaScript? It's actually a constant you can't redefine. But this was a concern. What if I defined undefined? I could make a lot of programs break in that way. Here's another one. Type of null is an object. That's kind of weird. Why is this? Well, I suspect this is one of the bugs that Brendan wanted to fix. He was famous for saying about two weeks later he's like, well, there's a bunch of bugs here in JavaScript. I'd like to go back and fix it. And he was told by management there's actually 40 developers working in JavaScript right now and we don't want to break them. I don't know. I bet 40 people on the call today would have rather that he came back and fixed bugs like this. Okay, that works type of NAN is a number. Except for NAN is not a number. How can not a number be a type number? Well, if we look at the types in JavaScript, we have objects, we have strings, we have numbers, we have dates. I would say that not a number kind of falls in the number category, more so than it falls in any other category. But yeah, at first glance it is kind of weird. These are the types of weird behaviors that we're going to look out today and start to understand a little bit more about how these things work. As we take a step back, let's look at the compiler. The compiler in JavaScript is this two phase compiler that allows us to do some really interesting things. Now this two pass compiler, will the first pass go look for variables that we can allocate on the second pass it will go execute all of the code. Now in modern compilers we also have JIT compilation, we have tree shaking, we have hot pass recompiling, and all of this is really helpful. But as we look at it, it still has that two pass compiler experience. Grab these slides from Robert.org and click on Read More or Watch more to learn more about the two pass compiler. Now if we look at this code, we can think like the compiler and we can start to figure out how it works. Now our first pass will look through for variable allocations and we find this one right here, varfu. So next pass we'll start to execute this code. We define our function, we call our function and then we're console logging Foo. Now we defined it in our first pass. It's not set yet. So when we console log here, we'll get undefined. In some other languages we might get a reference error saying this variable doesn't exist. But with JavaScript's two path compiler we get undefined. Variables in JavaScript are defined based on function scope. And so here in this if block we don't redefine this variable for the scope, but rather this variable is scoped to the entire function. We'll now set its value to bar and we'll console log that value and we get bar. Now this does look a little bit interesting on first pass, but as we think like the compiler, we can understand how this works. That's our task today is to think like a compiler and figure out these weird things. Now probably what we say to ourselves is variable hoisting. Instead of defining this variable here, let's define it over there. Now that's kind of what the JavaScript engine does, but well, this is a lie that we tell ourselves. Variable hoisting is a lie. It's a very helpful lie. It helps us understand JavaScript without having to remember this two phase compiler. But it is a lie. It's a lie that we tell ourselves so that we can understand this a whole lot easier. If we can think of this as hoisting, then we can start to think of it as a single pass, a single execution step. But yeah, really it's the two pass compiler. Let's look at this code here and let's think like the compiler. Well, we saw how we defined it and then we got undefined and then Foo. Let's do similar things, looking at defining and not defining variables in various scopes. So I've got two Foo variables. I'm going to define it here and here and then let's execute this code. What happens here? Take a moment to pause the video and figure it out or think through this example and see what you get. Now let's think like the compiler and see if we can figure it out. First pass, we go look for variable declarations here's one here's another one. Let's call this maybe the outer Foo and the inner Foo. So now we have two variables that are defined next pass, let's execute it. So our first step is setting the outer Foo to bar. Now we'll define this function and then call it as we call it. We define the inner Foo as bad. Now variables are defined to function scope, not curly brace scope. So this Foo right here is the same inner Foo variable. So we're overriding it from bass to Bam and we'll console log Bam. Then leaving the curly brace, our functions are defined to function scope and so we will log Bam as well. Leaving the function we're now console logging Foo and this Foo is the outer Foo right here until we get bar. Is that what you got? Let's do another example of defining things in interesting scopes. Now here instead of defining VAR here, I'm going to define VAR here what happens here. Let's think like the compiler and see if we can figure it out. Our first pass is to allocate all the variables. Pardon me, we've got an inner Foo here and we've got an outer Foo here. Ok, second pass, let's start to execute it. We'll set our outer Foo to bar. We'll define our function and call it now we're looking for a Foo variable. Now this Foo variable needs to be in the current scope or we may climb up other scopes because variables are defined scope to a function. We'll find this food variable right here that we allocated previously. Let's set its value to Baz. Now if true, let's jump in here. We're now overriding Baz to be Bam and we'll console log Bam. Then we'll console log the Foo variable again. We grab this Foo variable that was allocated previously and we'll output Bam again leaving the function. Now let's console log Foo and we'll find this Foo right here. So we get Bam Bam, bar. That was cool. We defined our variables in an interesting way and we can see how that affects our execution. Let's do it again. But in this case we won't allocate the variable here. We'll only allocate this outer Foo right here. What happens here? Let's think like the compiler and see if we can figure it out. Our first pass is to identify variable allocations. Now as we look for variable allocations, we only find this one food variable right here. Okay, next step, let's execute the code. So we'll set Fu equals the bar. Now we'll define our function and then execute it. We come in here and we look for a Foo variable. Is there a Foo variable defined in the scope? There isn't. So what JavaScript does is it climbs up scopes looking for a variable. So it will climb up to this scope and it finds this Foo variable defined right here. Okay, so let's set that to Baz. What was bar is now Baz. Now let's set that to Bam. We find that same Foo variable defined right here. So we'll console log Bam and then Bam as we leave the function. Now we're console logging the global Foo and we get Bam as well. So Bam, Bam Bam. That was cool. Let's do it again. But in this case let's not define any functions. What happens here? Let's think like the compiler and figure it out. Our first pass is to allocate variables. We don't have any. Okay, second pass. Our first step is to assign the Foo variable to bar. Do we have a Foo variable? We don't. So now we're going to climb up scopes. We may look through outer functions, we may look through other nested pieces in this file and ultimately we end up at the global scope and in this case we're not going to find a Foo variable. Javascript being really helpful in trying to make the code continue running, is now going to define a global variable. Okay, so now we have a global variable named Foo and we can assign it to bar. Let's define and then execute our function. We go looking for that Foo variable. There's no Foo variable declared here. We'll look up a scope, no Foo variable declared here and we'll wander until we get to that same global variable, that same Foo variable that was defined here. Okay, so what was bar is now bad. We'll overwrite it to Bam and we'll console log Bam Bam as we leave the function. We're now looking for that food variable and we locate that global variable again. And so we will console log Bam three times. That was cool. Now let's do something different. Instead of setting its value to a string, let's set its value to this Bam variable. What happens here? Our first pass. We go looking for variables to define. We find this one, this Foo right here. Our second pass. Let's go execute it. Okay, we execute this, we set it to bar. We'll define our function and then execute it. We'll go looking for this Foo variable and we find this outer Foo variable. So we'll set it from bar to Baz. Here we'll go look for the Foo variable. Finding this one again and we'll set its value to the value of the Bam variable. Let's go find a Bam variable. There's no Bam variable defined in this function. There's no Bam variable defined in this function. As we walk all the way up to the global scope, there is no Bam variable at all. Well, previously when we're trying to set its value, JavaScript could help and just create a variable. But here we're trying to read the value out of that variable. That variable doesn't exist and we get a reference error. Now at first glance that's kind of weird. If we set a variable that doesn't exist, it works. If we get a variable that doesn't exist, it doesn't work. But as we think like the compiler, that makes a lot of sense. The JavaScript compiler was trying to help us when it created that global variable. Is there value in creating it globally? We can debate that too, but when it's trying to read from a variable that doesn't exist it can't just jump in and help. Okay, so let's define this a little bit differently. Instead of Foo VAR, let's define these as let came into place with ECMAScript 2015 or Es six and it allows us to create variables that are scoped not to functions but rather to curly braces. Perfect. So what happens in this case? Let's think like the compiler and figure it out. Our first pass we define this outer Foo variable. We define this inner food variable and our second pass we're ready to go. Let's set outer Foo to bar, define our function and then execute it. Now we're looking for a Foo variable here on line four. Is there a Foo variable defined in this scope? There is not. There's this one defined in this inner scope constrained to this curly brace. But there isn't one in this scope. So we'll go walking up scopes like we did before. Oh hey, we found this outer Foo. So what was bar is now set to pass. Now inside the curly braces we'll define our inner Foo, set it to Bam and we'll console log Bam. Now outside the curly braces we go look for this Foo variable and we locate this outer Foo again. So we log Bam and then Baz. As we leave our function, we're now looking for a food variable and there is one defined in this outer scope. So we'll get Bam, Baz and Baz. Is that what you got? Now let's switch it from let to let's not define this outer let and only define this inner let. What happens here? Let's think like the compiler and figure it out. Our first task will allocate this inner Foo variable second pass. Let's go execute things. Okay, so we define our function and then execute it. We're going and looking for a Foo variable. Now we don't find any in this scope. We don't find any in this outer scope. So as we climb up the scopes we'll end up creating a Foo variable in the global scope and we'll set its value to Baz. Inside here we define the inner Foo and we set it to Bam. We console log Bam and then that inner Foo variable goes away at the closed curly brace and we're back here at console log Foo. Where is this Foo variable? Well, we defined a Foo variable in our global scope here. So let's output that here. We'll output bass as we finish our function. Now we're logging a Foo variable and no Foo variable was defined in this scope. So let's climb up the scopes until we find one and the one that we find is the global one that was created here. So we get Bam, Baz, Baz. Wait a minute, we just output a variable that was defined inside of a function? Yeah, at first step that looks really weird but as we think like the compiler we can completely see how that Foo variable defined globally ended up leaking outside of its scope. Let's do it again. But in this case we'll define these two variables. Here what happens in this case? Let's think like the compiler and figure it out. We have this interfo variable that will get allocated, this other inner food variable that will get allocated. We start by running our function and we'll set this food to Baz inside this if block we set our inner food to Bam. We'll console log Bam, console log Baz. Now what happens when we get out here? When we get out here we go looking for a food variable and while there isn't one defined in this scope, there isn't one defined in any other scope. We'll go looking for the global scope and we're trying to read the value of Foo. There isn't a variable and we get a reference error. Foo is not defined. Yeah, we're trying to read from a variable that doesn't exist and JavaScript can't help us with that. That's pretty cool. So by defining this variable here, we were able to stop that variable leaking into the global scope and being accessible outside of our function. Perfect. Now let's flip from let to Const. Const is new in es six or ECMAScript 2015 and allows us to declare a variable that won't change. Now it may define a variable that is an object and the properties in that object can change, but the object itself cannot. So what happens here? Let's think like the compiler and figure it out. We start by declaring a constant Foo and then we execute our function. We'll go looking for a Foo variable here. Now Const like let is defined by curly braces so we don't find this inner one. It doesn't exist yet. That's a nested scope. So we go find this one. Now we want to change its value from bar to bass, but it is a constant. And so our program stops here. Type error assignment to a constant variable. Now this is perfect. In ECMAScript 2015, in es six our program will stop. Well, if we're using Babel or similar transpiler to get from es six to es five, then in es five there's only a VAR. There is no Const. So this will actually override this bar and will continue on the way we expect. If we're using Babel to transpile into es five, this code will work. The moment that we flip this code from es five to es six as our target output is the moment that our code breaks. The Babel compiler didn't break, our code was written badly. And so as we define this function, then we need to define a different variable here. That was fun to go dig through scopes and understand various places where we could define or not define variables and the interesting behavior that results from that. Now let's take a look at this. This is an interesting construct. I love this definition of this. This is the thing to the left of the dot and I love how this definition works out so well. Read or watch more. Grab these slides from Robert.org and click through the blue links to dig more into what is this? So let's dig out some code and play with this. How can we control it? What does it mean? Here we have this speak function where we grab this name. Now with this speak function we'll use this name and this is defined as the thing to the left of the dot. So what happens in this case? Now I could have defined this speak function in each of these objects, but I just defined it globally so that we only need to define it once. Now I'm going to start out and define this global variable called name. So when I speak here, what's the thing to the left of the dot? Well global. So I'll get Ninja right here. When I call Ops one speak the thing to the left of the dot is OBT one. I get Doctor OBJ two. I get Skywalker Ninja. Doctor Skywalker. That worked out pretty well. Now, let's play with it a little bit. Let's say obspeak equals object one speak. Now, as I call object. What's the thing to the left of the dot? Well, yes, I know this method was baked into this object, but now it's just a function that's out here. What's the thing to the left of the dot? Global. And so now this is Doctor and this is undefined. Yeah, probably. I'll get a reference error instead. Okay, so, well, let's take that knowledge and put it towards you in this button, click event handler. So I'm going to take document, get element by a D. I'm going to go grab the button and then I'm going to add this event listener. So when they click, it will speak, it will run this speak function. And this speak function is going to call this ID. I know this ID is defined because I got my element by ID. So when they click what's the thing to the left of the dot here's? The dot. And the thing to the left is this button. Okay, so I speak and I get the button. Perfect. Now, I want to modify this code slightly. Instead of just saying the button right away, I want to do that 100 milliseconds later. So I'm going to create this set timeout and I'll pass in a new function and I'll log this name. Now, what happens here? Well, when I speak out here, I will go call this in 100 milliseconds. I'll go looking for this name. Set timeout, creates a new scope here when this function runs in 100 milliseconds. So this is the global name defined here. So I'll get Ninja. What happens when I call odds one dot speak. This is also the global because the thing to the left of the dot here is, well, global. So I get Ninja twice. That's not what we want. Let's see how we can control this by using some techniques. Now, here's this call function. Now, this call function allows me to execute a function passing in what I want this to be. I'm overriding this. So I haven't defined this speak event here. Instead, I've speech function. Instead, I'm just calling OGD one speak and saying call. It passing in the this as object two. So when I get here, I've already overridden this. I've passed in this as this OBS too. And I get this name. This name being OBS two. And so I get Doctor and Skywalker. Perfect. Now, let's introduce that set timeout piece. Now, we wanted to do a dot call to be able to override what this does. And so now, instead of just grabbing a new scope, we'll specifically override the scope. Now, what's interesting, as I call OBT one speak, it goes into set timeout and in 100 milliseconds, it returns the result of this thing. Well, I've defined the function and then I've immediately called it overriding this. So I get the correct name. I get Doctor, but I don't get it in 100 milliseconds. I get it immediately. I specifically called this function right away. Now, I don't want to call it right away. Rather, I want to call it in 100 milliseconds. Let's take a look at another technique. Now we have Bind in JavaScript and we can pretend that Bind kind of works like this. This is a fake bind. I'll pass in my function and I'll pass in what I want this to be. In this case, I passed in that and we're going to return this new function that I can call whenever I want. Now, I still have this set the way I want because I call function call and I pass in that. And now I've overridden what I want this to be in a spot where I can then call it later. We can identify this as a closure where I'm going to close over that variable and this function variable so that when I'm ready to call it, I can call it later. So I'll call my fake bind. I'll pass in my speaking and my object too. And now if I call object two, Speak, because I've kind of built this envelope of this baked into place, then it will work as I expect. So let's do that. Let's grab OBS to speak as object one, speak bindobject two. And now, yes, I get the result that I want. Skywalker. Perfect. Now we may find a lot of code here where if I do define my speak method here we have a bunch of code that says Obst two, speak bindobject two. Yeah. What's it doing? Well, it's very specifically hard coding this as OBS, too, in all of my functions. That way, if my function gets called from a button or from another object, it will still bind this correctly. I've overridden it and then reset that function into place. So let's use Bind here and let's use Bind to see if we can do this. Now Bind will return a new function that we can call later. And so when we call this in 100 milliseconds, then this name, this is overridden by the list that was right here. So we're taking the scope that we had when we started the speak method and we override it so that this new scope will have that as well. And because this returns a function we can call later, when set timeout runs in 100 milliseconds, we will get the correct answer. Ninja and Doctor, we've used Bind to be able to fix our scope problem. This now behaves as we expect. Now let's look at arrow functions. Arrow functions are great because they are a lot simpler. We only have four characters instead of function, which is this big. So let's call Bind and see if we can do it. Now, the other thing that Speak does is it defines the list at the point where the function is defined. So it locks in this at the point where it's defined. So now we have console log this name and we're going to bind this. And that's kind of how arrow functions work is it just calls bind right away. We're sticking this in this envelope and creating this new envelope that we can call whenever we need to to be able to get this already set. So let's come back into our arrow example and we'll use an arrow function here. Now the arrow function is going to capture this at the point where it's defined and so it grabs this name right away. So speak works as expected. We get Ninja opt one speak when we call that this was defined as the global object when it was created and so we get three ninjas. That wasn't what we expected. Hey, we learned about this bind thing. Let's go do that. Let's say Obst two speak equals Obst two speak bindobs two. Nope, we still get three ninjas. We stuck this envelope inside of another envelope and as we started opening all the envelopes, the last envelope that had this set had this set in the global scope. So in this case, this arrow function didn't help us. The arrow function bound this in a way that we didn't expect. Okay, so let's come here and use an arrow function to see if we can bind this correctly. Okay, we've bound this and as we call it with odds one speak, this is set to odge one. We'll go grab that as we find this function. And yes, now we get doctor it behaves as we expect. This error function was perfect in being able to capture this and pass it through to the new context created by setTimeout. But notice that we're not defining function here as an arrow function. If we were to find this as an arrow function, then we would capture this at the point where that was defined and we would always get the global scope. That was fun to dig into this. This is a really fun thing to play with in JavaScript and it's one of those places where it does get kind of weird, but we can control it with bind and apply and arrow functions to create the specific envelopes of capturing those variables that we need. Next, let's take a quick look at the event loop. I love this website here that starts talking about how the event loop works. Now we can paste in our own code here, but I'll just run the code that they have as it runs each step, it will run it and then if there are any side effects, they will go out here into that box that waits for those side effects to complete. Now once the call stack is empty, it will go look in this callback queue to pull in any data that we need to now for example, this on button click is waiting for a click to happen. If I click it now, that ends up in the callback queue. And once this stack is empty, then it will grab that and be able to do the things that it needs to. So we can take a look at this code and we can paste it into place there and take a look at how it works. For the sake of time. I will just say that because we have set timeout and we're not capturing this I variable, then what we're going to output here is ten instances of number ten. But getting to watch that is really fun. And so we have this stack where we're doing the current execution. We have the callback queue and we have these things waiting to go. Read more or watch more. Grab the slides from Robert.org to click through as we look at Node. This is the logical implementation rather than the physical implementation. Node is this pointer to a particular request and it will empty the stack and then it will move to the next one. If the stack is empty, it will grab the callback queue and finish that before moving to the next one. So it's going to go solve this request and then solve this request all of the things in the current stack and it'll move around the circle. Well what if this one is counting from one to 1 million? Well then the pointer is going to finish up that stack until it finishes and then move on to the next one, which means all of these requests are stuck. Now JavaScript being single threaded, it's not that they're stuck, it's that the pointer is over here. Emptying this queue instead. Well how can we control that? We may do something like this, set time out of with zero milliseconds. Why would we wait zero milliseconds? Well, our goal is to yield the request queue so that the Node pointer can run through all the rest of the request and solve theirs before coming back in more modern ways. We can do process next tick and pass in either an empty function or nothing. And now that will yield the thread for a second. If we're going to count from one to a million, we should probably process that next tick a couple of times in that loop to make sure that all of the other requests are responsive. The Node event loop. That was really fun to look at the event loop. Now let's look at Async and Await. Async and Await is one of those things that was clearly stolen. I mean inspired by C Sharp. In C Sharp we have the task parallel library that via Async and Await creates this state machine. Now it's a resumeable state machine where we know what step we left off on and we can come back to it and pick up and continue on. Similarly, in JavaScript we have this state machine that's built on top of promises and generators. That resumable state machine we can pick up and continue on. And so we have code that looks synchronous, but it actually works asynchronously. So here's some promise code. Now we have a library that returns a new promise. We have a resolve and a reject. After we finish our long running task, we can call resolve, and so then we can do this. Then calling various libraries and Catch to handle our errors. Now that's cool. Promises were really helpful in being able to get away from the nested wing of Callbacks. When we flip this to Async and Await. Now we'll just have an Async function. We'll return those results and now we can await calling that data. As we look through this function, it looks pretty synchronous. That's cool. We can almost pretend that it's happening straight away because of this resumable state machine. Now, as it calls into the library, it will yield the thread. And once that library finishes, then we'll get back to this result. I can just use try Catch again. Now I don't have to do a Then and catch, so it's a much cleaner user experience for await. Do we need to wait until we upgrade our whole project to use Async and Await? We do not under the hood promises. Async and Await uses promises. So if we have this Async function that will return a thing. If we don't await it and we just get this response, we can take a look and response is a promise. Now the beautiful thing about that is we can dot then a promise. So let's take this legacy. Let's take this new function, this Async function, and we want to call it from our legacy code. So I'll just take this response and I'll say then and I'll continue on with all the things. If I have a then experience in my thing, I can still call my new Async function. That's perfect. Let's take it the other direction and say, well, what if I have a new Async function and I need to call into an old Promise function? Well, the result of a promise is a thing that we can await. So let's await this promise. The interrupt story between Async and Await and Promises is really elegant because Async and Await builds the state machine based on promises and generators. Whether I'm replacing a method that I want to call with a new Async and Await method, or whether I'm replacing the call, the main body that needs to call into a bunch of promise based code, I can interoperate between Promises and Async and Await really easily asynchronously because Asynchronously is based on promises, the Interop is just built in. It is really cool. I can await a promise and I can dot then an Async method. So how would I do many things in parallel in Async? Now that I kind of understand that Async and Await is promises under the hood. I've got this library that is an async function, and I want to call it but notice how I am not awaiting it. Instead, all three of these are promises. Now that's great. Let's scroll down a bit and see how we can do it, knowing that they're all promises. I can take an array of promises and I can say promise all and that will return to me an array of results. Once that's done, now I can await that, and now it will unwrap those. So I took those three promises. I awaited them all. And once all of them are done, or one of them errors. Now I've got my results. We were able to do these three things in parallel, understanding that async and awaits is based on promises. Perfect. Now the interesting thing here is that it might be kind of weird to flip between async and await and promises. You may need to document this a bit before your coworkers understand what's going on. That is totally fine. We saw how async and await worked out really well to be able to interrupt between promises and async and await. We saw then how we could control it with apply and call and find and arrow functions. And we saw how defining and not defining things worked in really interesting ways. Now that we can think like the compiler. Yeah, job script is pretty cool. Not bad for a ten day project 25 years ago. You can grab these slides from Rob Rich.org, and if you're watching the presentation later, hit me up on Twitter at Rob and let's continue the conversation. For those of us who are here live in the conference, what are your questions? What are your thoughts? What things did I get wrong? Hi Rob, thanks a lot. I really appreciate that for sharing your talk today. We do have a few minutes to review and answer some questions submitted in the Q and A function. So let's check that out. Yeah, definitely. I saw one question come through which was great. Do I need to wait until we upgrade our whole project to use asynchronous wait? Great question. And the answer is no, just doodle them in. And the Interop between Async, wait, and promises is perfect. Let's see, there's another one here that says what's the difference between dot apply and dot bind? Good question. When we looked at bind and let's go there to no, not there. When we looked at call that calls it immediately. By comparison, bind will return a function that calls it later. So here's a fake bind that I've kind of invented that starts to tell this story. We would say bind and pass into this, but we can think of it as returning a new function that does that call and apply work exactly the same where they execute the function immediately. Call, you pass in an array of parameters and apply you pass in each parameter, one at a time or I've got that backward by comparison bind will return a function that you can call later to be able to set this the way you expect. Great question. Let'S see, I think we have one more. Let's see, have you posted somewhere good testing patterns for asserting functions using async awake good question. I actually have an entire talk focused on testing asynchronous patterns and it is really fun. Javascript test and node the browser and CI. Perhaps we'll have me back for a talk on that. You may be able to find the video but you can definitely find the code up on GitHub where I talk about async and await patterns. The important thing is to make sure that in your test you await the result because if you just run through all of the things then the synchronous part may pass but the asynchrony is still running. In a similar way, you probably want to close your database connection at the end of your test if you're doing integration tests because otherwise your testing framework will hang waiting for that final wait to finish. Great question. And let's see one last one says should I flip all my functions to arrow functions? Good question. Should I flip all my functions to arrow functions? We saw when we were doing this that because we had flipped all of our things to arrow functions we actually got the wrong results and we couldn't fix it using another technique. We had already packaged this into that envelope and so even packing that into more envelopes wouldn't override the thing. If you just flip all your functions to arrow functions then you may get the wrong result instead. Choose carefully when you want to use the benefits of arrow function. It is smaller but it also binds this and so if you don't want to bind this as you create the function then you probably want to just use the function keyword instead. Okay, well thank you so much for your talk and for answering all those questions.