What do you want to learn?
Leverged
jhuang@tampa.cgsinc.com
Skip to main content
Pluralsight uses cookies.Learn more about your privacy
Reasoning About Asynchronous JavaScript
by Wes Higbee
This course will help you learn to take advantage of JavaScript's single-threaded nature, as opposed to being caught off guard by it. You'll walk away being able to effortlessly reason about when things execute in JavaScript.
Start CourseBookmarkAdd to Channel
Table of contents
Description
Transcript
Exercise files
Discussion
Recommended
Overview
Questions
What do you think will happen when I click this button here on the right-hand side, which by the way this button wires up to executing this event listener over here on the left-hand side? Take a minute and look at this code, pause the video if you need to, and think in your mind, think through what you think will happen. Unfortunately, I'm not going to give you an answer yet because I'm not going to push the button, we'll come back to this code sample later on, and we'll discuss this in this course. There's something about this code sample that expresses a problem you've likely run into in the past, or is similar to a problem you've had. Do you have a lot of experience working with other languages that are multithreaded? Have you ever wondered how a JavaScript engine executes your code, or more specifically when your code is executed? Have you ever tried to do things in parallel in JavaScript, or any other language? Have you, say for example, tried to run parallel web requests to speed up your application? Did you run into any trouble doing that? Have you ever had any computationally intensive JavaScript code that locked up the browser? It is possible right now in some of your real applications your users are experiencing this, and you don't even know it? Have you ever thought about the complexity of the code that you're writing, and whether or not it might take too many resources in the browser? Have you ever wondered why race conditions don't seem to be such a big deal when it comes to JavaScript? Have you ever heard that JavaScript is single threaded, and found that off-putting? Do you think that's a weakness? Do you find callbacks to be confusing? Have you ever wondered why these are even necessary? Have you ever wondered what any of these terms mean? Have you heard of event loops, running to completion, non-blocking I/O, and cooperative concurrency? If any of these questions piqued your interest before, or now, then you're watching the right course to get some answers. The simple reality is most of us that write JavaScript come from working with other languages. We use JavaScript just enough to get the job done on the front end of our applications. We rarely take the time to really understand JavaScript as a language, how JavaScript engines work, and how various different environments affect the JavaScript applications that we write and execute. And that can be a big problem because JavaScript, unlike many other languages, JavaScript is single threaded, and its concurrency model is different as well. Most languages we come from have preemptive concurrency, JavaScript has cooperative concurrency. All that mumbo jumbo basically means when code executes in JavaScript is quite a bit different than when code executes with other languages. I've designed this course to help you wrap your mind around asynchronicity in JavaScript. Or more specifically, how to understand how code executes in such a way that you can reason explicitly about when your code will run. Once you're able to reason explicitly about code execution in JavaScript, you'll be able to avoid many of the problems you might have if you try to apply the multithreaded mentality to JavaScript.
Who This Course Is For
I've designed this course for anybody that has some familiarity working with JavaScript on the front end of your applications, or JavaScript on the back end with Node.js. There are really two things you should be comfortable with to be able to understand the content of this course. First, you need to have a basic understanding of JavaScript as a language. If you can read this code sample here, the same code sample I asked you a question about earlier, if you can read through this, and reason through this, you're ready for the contents of this course. The second thing you need to be comfortable with is passing functions as arguments. And guess what, this code sample does just that. We're passing a function here, as an argument, and this function will be the function that's invoked when somebody clicks the button on the page. So if you're at least comfortable reading through this code sample, then that means you already understand passing functions as arguments. Beyond these two things, there's nothing else you need to work through the contents of this course. I don't believe there's a skill level that's required, I don't even believe in skill levels, I just believe in a desire to learn. So if you'd like to learn how to reason about asynchronous JavaScript code, then this course is right for you.
How to Use This Course
Learning is doing. You will not learn by watching this course. You will not learn by reading through the supporting materials. You'll learn by practicing, and you'll ultimately learn by applying what we're talking about in this course to real development projects that you work on. I've created quite a few challenges in this course where I'm going to ask you to try something on your own, and then come back and I'll explain how I approach the problem. I really encourage you to take the time to work on these challenges. In addition to these challenges, look for opportunities to apply these ideas in real code that you're working on. Find existing code that has problems that are synonymous with what we're covering in this course, refactor that code, and see what you think of the result. It doesn't mean you have to push the refactoring into production, but if you take the time to apply this to real code you've authored, to real problems you've faced, you'll learn these approaches in no time.
Links to Examples
To work through the examples in this course, I have a bunch of Plunkers created, and I've created the links here for you as a reference on the slide. As I work through the various different examples, you can refer back to this set of links if you want to follow along. Make sure you use this set of links, there are a few Plunkers that were out of date that I might have used in the actual course, these are the latest versions of each of these. Sometimes I made some changes in the course, and then saved those as new Plunkers, so just refer to this set, and try to refer to them by the demo name here, which is the black part, and then the Plunker link below. Throughout the course I'm using the OpenWeatherMap for several of the examples to demonstrate making web requests. If you'd like to follow along with those you'll need to sign up for an account, and get your own API key. The API key I use in the course will be expired by the time the course comes out, so you won't be able to use that. You'll need to get your own API key if you want to try out those examples, and then you'll just need to replace the API key in the sample.
Reasoning Explicitly About When Code Executes
Intro
There are only two modules in this course, because the first thing I want to do in this module is help you understand how to reason explicitly about asynchronicity in JavaScript, which means I really want to help you understand when your code will execute, and how to reason explicitly about that. Once we've done that, then in the next module we'll pack in a ton of extra examples to work through to apply the concepts we learn in this module. In other words, we'll take these tools and see if you're ready to go with reasoning about asynchronicity. In this module, I'll have a series of key takeaways for you that will ultimately allow you to begin thinking explicitly about the asynchronous seams inside of your JavaScript applications.
Single-threaded
So first things first, let's talk about the concurrency model in JavaScript. I want to come back to this MDN page because I saw something on here that gets at the heart of why this is so important to discuss. If you've worked with other languages, then this second sentence here very likely applies to you. The model in JavaScript, the concurrency model, is quite different than a lot of other languages. If you've worked with Java, or C#, any of the .NET languages, Ruby, or C, or any other multithreaded language, then you very likely have what I like to refer to as a multithreaded mentality. Most banks have several different lines that you can get into with several tellers that are servicing customers. So most banks, like most languages, can execute code concurrently, at the same time. So just like a bank with multiple tellers can accept deposits and provide withdrawals from bank accounts, most languages can have multiple threads that are executing code at the same time. This is not the case with JavaScript engines. JavaScript engines can only execute one piece of code at a time, that's like a bank with one teller, or a bar with only one bathroom. And of course, I'm sure you can imagine that this can cause problems. But before we talk about the problems, you also should know that this can be in fact a very good thing. If you've worked with multithreading before, you know you can run into a lot of problems when more than one piece of code can execute at the same time. JavaScript's single-threaded nature avoids many of these problems. In this module, I'll have a series of key takeaways. The first of these is that the JavaScript engine that executes JavaScript code is single threaded. And again, this is like a bank with one teller, or a bar with only one bathroom.
The Event Loop
If you only have one bathroom in a bar that means only one person can go to the bathroom at a time. Obviously then there has to be a queue to hold the other people that would like to go to the bathroom. Just like a line will form at the bar outside of a single bathroom, in JavaScript there's a queue where work queues up to be subsequently executed. That queue is a part of what we refer to as the event loop in JavaScript. This is where work is stored until the current operation or task is done executing. In this wonderful visualization, out on MDN, the queue is a pivotal component of the JavaScript execution engine. Work is added to the queue, and it sits there until whatever's processing is complete, and then the next item is taken out of the queue, just like a line at a bar, when somebody gets done going to the bathroom the next person can use it. So the next key takeaway is that JavaScript has this event loop that contains a queue that's just like the line outside of a bathroom.
Non-blocking
I'm sure you've been to a bank or a bar before where there's only one line and one bathroom or teller, and you've been really frustrated because that line takes forever. And now you might be thinking, hmm, okay, so JavaScript can only run one thing at a time, and everything else queues up. That explains why it's so darn slow. Well it turns out, that's not the reason why JavaScript can be slow at times. Unless, of course, somebody has made a mistake, a mistake that's probably because they wrote a program without really understanding the single-threaded nature of JavaScript. They probably wrote a program coming from the multithreaded mentality when they should have had a single-threaded mentality when they're working with JavaScript. Just because we can only run one piece of code or a task at a time doesn't mean things have to be slow. To understand this, let's walk through an example. If you want, you can open up this Plunker and follow along. Throughout this course I'll have several Plunkers or JSFiddles that you can use to grab the example I'm working with, and play with it yourself. Feel free to grab this, and work through this with me. There is some supporting code around the function we're going to look at here, but you can ignore most of that. Right now what I want to focus on is the heart of this example, which entails this load function. When this script is loaded, the very first thing that happens is we grab this city element, and we set its value equal to New York. I'm doing that because when you run this script, you'll see there's an input text box, that's what I'm referring to here, I'm just hardcoding the value to New York, that way I don't have to type in a value to test this application. Once we run the app we get back weather results, we get the current weather, and we get the 5-day forecast. Now you can change this city if you'd like, and then click the Load button to load that city's weather. When the script loads though, we go ahead and call load with New York, and this load function is executed. Now if I scroll down here, we can talk about this. First, the load function grabs the city value. It shows the results loading, so if we click Load you'll see loading briefly shows, it fires pretty quick there, then I set up some variables to point at URLs to grab the weather, which is the current weather, and then the fiveDayUrl, which is the 5-day forecast. So there are two requests that fire off to load the current weather and to load the 5-day forecast. So the next block of code, these two chunks of rather large code, fire off two web requests. When the weather request completes successfully we call this weatherSuccess function, and when the fiveDayRequest completes successfully we call this fiveDaySuccess function. These functions just parse the JSON that comes back, and build the summary that you see on the right-hand side here. If anything goes wrong, both of these requests refer to a failure function, and we just show an error in that case. For example, if we mess up the URL you'll see, Oh no, something bad happened! So I just said that only one thing can run at a time, but that weather looks like it's loading pretty quickly, and it's not just because I have an amazing internet connection. There's another reason for that. But if we look at this code on the left-hand side it may not be obvious. According to what I just said about JavaScript, when we request the current weather it might be tempting to think that we have to wait for the current weather to come back before we can request the 5-day forecast. But, I'm sure if you've worked with callbacks you're familiar with the fact that callbacks allow us to initiate a request, for example to grab the weather, and then continue the execution of the rest of our program. In this case then initiating another request, and then continuing on, and then when each of these requests are complete the respective callback function will be called, for the current weather and for the 5-day forecast. But how in the world is this possible if only one thing can execute at a time, how can we make two web requests concurrently? Well first, before we talk about that let's pull open the debugger for the browser, on a Mac you can type Option+Cmd+I, on a PC F12, that'll work for most browsers. Let's go to the Network tab, and let's see what happens here when I stop, and I'm going to clear this out, and then Run our script again. We've captured the requests here in the timeline, so we can take a look at these. Your results will probably vary, but if you come over to the right-hand side here there should be a point where you see two bars in parallel, and if we zoom in on that by clicking with the mouse, and then dragging to select here, we'll pull open a window with just these two requests in it. And if we look down here at the bottom we can see, if I expand the name here, we can see our request for the current weather, and we can see our request for the 5-day forecast. And if you look here in the timeline, these are executing at the same time. How is this possible? If we can only do one thing at a time, shouldn't we need to get back the current weather before we can ask for the 5-day forecast? While the JavaScript engine will only execute one piece of JavaScript code at a time, behind the scenes there are a pool of threads that are used for things like making web requests. And this pool of threads can have multiple connections open to multiple different servers to request data for multiple different requests at the same time. This is all hidden behind the scenes, but this is how we can still achieve parallelism within a JavaScript application. We still have, behind the scenes, the ability for multithreading, it's just not something that applies to our JavaScript code itself. What we're talking about here, or what we're referring to, is the non-blocking nature of JavaScript. So we make a request for the current weather, but we don't wait for it to come back. We then make the next request to get the 5-day forecast, but we don't wait for that to come back either. At this point we're done executing our program, we're not blocking to get the results. If we did block, wow, that would be pretty bad for the fact that we can only run one thing at a time, because that would mean we have to request the weather and wait for it to come back, then the 5-day forecast, wait for it to come back, and then we can work on whatever we want after that's complete. So the next key takeaway is that JavaScript supports concurrency by not blocking for I/O, and various other things as we'll see throughout this course. The non-blocking nature of code in JavaScript allows us to still have performant programs, because we don't have to wait for results of long running operations to complete, like a web request or opening a file. Have you ever been in line at a bank and somebody forgot their ID in their car? I'm sure you'd be really frustrated if the person in front of you, working with the one teller that's available, went out to their car, got their ID, and then came back, and the whole time you couldn't talk to the teller, you had to wait for them to get their ID. That would be an example of a blocking program. If something else is needed, and a program blocks the queue until it receives it, obviously things won't be very performant. And in the case of a bank, people will be very frustrated. It makes much more sense if somebody forgets their ID, or paperwork, that the teller will service the next person while that person goes out to their car to get their ID. Once they get back then the teller can pick up where they left off. That's a nice way to think about the non-blocking nature of JavaScript.
How to Avoid Blocking
When you're writing JavaScript programs you need to be cognizant of the blocking or non-blocking nature of the code you're creating, because even though JavaScript supports non-blocking constructs, that doesn't mean you couldn't accidentally write something that's blocking. If there's a line of people waiting to use a bathroom, it's not a good time to pull out the newspaper while you're going to the bathroom. Let's take a look at what this is like in a code sample. Here's what this looks like in our sample of loading weather data. The third parameter to this open method, when we're making a web request, dictates whether or not that request is fired asynchronously. Right now this is set to true, which means the request will be asynchronous, or non-blocking. If we simply toggle this to false though, now we'll block and wait for the request to complete before we continue the execution of our program. And just to help you see what this looks like, let's put in some console logs in here so we can look at the order of execution of our program. So I'll put in a console.log 1, console.log and 2, and then we'll put in a console.log 3. In addition to these console log statements, we also log out the data that we get back from the API in both cases. So, just to make sure we understand which log statements are logging out which chunks of data, let's put in a little string describing that this is the current weather, and that this is the five day forecast. Okay, I've opened up the console over here in the browser, let's go ahead and run our program. Okay, if we look over here in the console, ignore the first two entries, here is our first console log statement printing out 1. We get a warning about synchronous requests being deprecated, because of course this is something you would not do in your real application. Right after that warning, we get back the current weather, which means that the weatherSuccess function was called, before we log out 2, you can see 2 is logged out after that. And then we have the five day data that comes back, before we log out 3. So we indeed are blocking when we make these requests here. If we want to contrast this, let's switch this back to true, clear out the console. Now you tell me what do you think the order will be in the console? What do you think will be printed out? I'll stop this, clear the log, and then run this again. All right, we have a different order this time. Was this what you expected? We get 1, 2, 3, and then we get the responses for the 2 different requests that we made. So when we make non-blocking requests our program continues to execute, firing off both requests and printing out all these log statements, and that all happens then before our callbacks will be called to process the resultant data. So the key takeaway here is to make sure that you avoid blocking your application. When you make web requests make sure they are asynchronous, in Node.js if you're loading a file make sure you do it asynchronously. And these are pretty obvious, but one that might not be so obvious is making sure you just don't hog the processor. Don't hog the bathroom, that's the most common way you'll end up blocking your application. And what I mean by that is make sure your program isn't CPU intensive. For example, don't do a Monte Carlo simulation in your application, and if you do need to use one take a look at web workers, which are a way to run background code in a separate JavaScript engine so that you don't block the front end of your application.
Run to Completion
Let's take a look at another example of what happens when we block in our application. I just mentioned that the one case you really need to watch out for is a long running operation, for example a Monte Carlo simulation. These are often a way you can end up blocking your application without knowing it. So in this example we do two things. This is a new Plunker by the way, you can grab this one and follow along if you'd like. We have a button on the page, so when we run this we'll see a button. When we click this button we execute this button's listener. This button's listener does two things: first it modifies the page, it sets the background color to red, and it adds some text to the page. Then, before this operation completes, a loop is run, and this loop simulates an operation that takes 10 seconds to complete. So we have 10 seconds here, and we just take a look at the time when we start, and the time as we're going, and once 10 seconds has passed, then this code will be complete. So this would be like running a Monte Carlo simulation, or blocking while we receive a web request. Before I click this button, what do you think is going to happen when I click it? Take a minute and think about that, pause the video if you need to. Okay, let's go ahead and click this. When I click that the button turns blue, it's not depressing, and if I try and select anything over on the left-hand side, I can't select anything. After 10 seconds pass, then the background color turns red, we add the text to the page, and now I can select things over in my editor on the left-hand side. I'd really encourage you to pull down this Plunker and try this out. This is a great example of a blocking operation. We are literally hogging the bathroom when we wait for 10 seconds. Nothing else will be able to execute, which means we can't interact with our Browser tab anymore. We can interact with another one of the tabs in the browser because each tab has its own process, and so each tab has its own JavaScript engine, but our JavaScript engine is blocking on this long running operation. So this is why you want to avoid long-running operations. But we've already covered that key takeaway, I want to point out another key takeaway as a result of this example. This event listener, when it fires, it runs to completion. If it takes 10 seconds to complete, nothing else will be able to run during that 10 seconds. This points out another key takeaway, and that key takeaway is that when a chunk of code runs, that chunk of code will run to completion. So if it blocks, until it's done blocking, nothing else will be able to run. This is a restatement of the non-blocking nature, and the fact that you should avoid blocking, but it's something that you really want to keep in mind, because understanding this will help you understand how asynchronous code works in JavaScript. In our bank analogy, run to completion will be much like the fact that a teller will make sure that each customer is happy before moving on to the next customer. So if it takes 10 minutes to help one customer, all the other customers have to wait. Again, if you need to do something that takes 10 seconds, take a look at web workers. Web workers, in this case, would be a lot like opening up another line at the bank if somebody is taking a long time. This run to completion nature of JavaScript is in stark contrast to other languages you may have worked with. If you've worked with a multithreading paradigm, then you know it's very possible that a chunk of code could be preempted. Preempting is essentially interrupting your code, wherever it's executing, to give the thread to somebody else. In a bank situation that would be a lot like taking a customer that's taking a long time and kicking them out of line, and putting them back in the back of the line. It may not be done, but if other work needs to be done you push them to the back of the line so that you make sure you're still getting other work done. However, in the case of JavaScript we don't have that, we have run to completion instead. So if a customer takes 10 minutes, everybody waits 10 minutes. Obviously this can be a problem. Let's hop back to the previous Plunker we were working with with the web requests, where we logged out to the console. I want to point out one more thing about run to completion that you need to be aware of. When we make these web requests, and they are asynchronous at this point because we have true for the asynchronous flag to the open method, when these execute asynchronously all this code does is basically set up the request and fire it, and then we set up the request here and fire it off. One thing that we often forget because we come at JavaScript from a multithreaded mentality when we need a single-threaded mentality, we often forget how these log statements will execute, which means we often forget how run to completion actually works. What do I mean by this? Well, let's open up the debugger again, let's stop our program and run it again, and now let's take a look at the results. We already saw this, but I want to point this out again. We get 1, 2, 3 in the console, and then we get the result for our web requests. We will never get the results of these web requests in the case where we're running them asynchronously, we will never get those results interspersed with our 1, 2, 3 logs, because our load function entails all of this code right here. So all of this code will execute before we ever get back and process these web requests. Now, down here this might be a bit confusing, but all that's doing is declaring this weatherSuccess function. We're not executing the code inside, we're just declaring the function. Nonetheless, we will execute our log statement for 1, 2, 3 while sending off our 2 requests, actually let's run thought this again. We log out 1, we fire off our first web request, we log out 2, we then fire off our second web request, we log out 3, we declare the weatherSuccess function, the fiveDaySucess function, and the failure function. All of this executes before we ever get to our asynchronous web request results. And that's because the handler for these web requests is pushed into the queue when the request comes back. Now more likely than not, our program execution will complete before the requests ever come back to us because these are web requests, and they take a while. But if this web request where somehow instantaneous it still would go into the queue and have to wait until we're done setting up our web requests. Before we move on, one thing I would like to point out, the function declarations. These are actually hoisted, so technically my description was inaccurate, these will be declared before we ever process our web request, but that's something you don't need to worry about. The important part here with run to completion is that our log statements will all execute before we handle any of our web requests. More importantly if we add any other code down here, this would be executed as well before we process our web request.
Challenge - Run to Completion
Now it's time for a challenge. Using this Plunker example we've been working on with the parallel web requests, I have a question for you. And you can grab this Plunker if you want, or you can just think about this. What happens when I grab calls to open and send for the weather request? What happens when I grab these, and move these above my definition of the handlers to handle the result? Same thing with the fiveDayRequest, what happens when I move these two lines up? Will we have any problems because I'm defining the callbacks after I fire off the request? Is it possible that the request would complete before these callbacks are set and cause a problem? Let's go ahead and run this, and see what happens. As you can see it looks like everything still works. Does that surprise you? Maybe that one was a little bit easy because we already talked about this one in terms of run to completion. Let's take a look at another example. This one is rather abstract, and I apologize for that. I like realistic examples, but I think this is a good quiz to see if you really understand what's going on with asynchronous operations. So you can grab this Plunker if you'd like to play with it. But, I again will ask you, what will print out in the console when I run this program? By the way, setTimeout is a function, much like a web request, that schedules another function to be run later on. So setTimeout initiates an asynchronous function later on. And in this case, I'm specifying a timeout of 0 ms, so no delay. After 0 ms please run this code here, and after 0 ms please run this code here, actually that's in reverse order. So you tell me, what will print out in the console? All right, let's go ahead and run this, and you can see in the console we get 1, 5, 2, 4, 3, is that what you expected? We get 1, 5, 2, 4, 3 because of run to completion. We can't interrupt a chunk of a code when it's executing. So this entire chunk of code executes before we can execute any of these asynchronous functions. What does that mean? Well, okay so here's what's happening with this program. First, we log out 1, then we set a timer for 0 ms, though actually the browser specifies a minimum default of 4 ms, but you can ignore that because it doesn't matter even if it was 0 ms. A timer is set for that duration, and then after that timer executes, or expires, this function will be pushed onto the event loop's queue, but it can't run right now because we haven't completed the chunk of program that we're executing because there's one last statement, and that's to log 5. That's how we get 1, and then 5. Once 5 is logged out this chunk of code is done, and the next chunk that's waiting is this chunk of code, because the timer will have elapsed, and that's why we then after 5 get 2 because console.log of 2 runs next. We set another timer for 0 ms, and then we log 4. Once this chunk of code is done, this function is done, then we can execute this last function that's sitting in the queue, and in that function we print out 3, and that's all we do. Run to completion is kind of weird coming from a multithreaded mentality. It's not too difficult to grasp this single-threaded reality, and in fact this is a really nice feature to only have one chunk of code running at a time, and to not be able to interrupt that code. There's nothing that says that a single-threaded application couldn't possibly have preempting, like is common in multithreaded or multi-tasking applications, but in the case of JavaScript we don't have that. We don't have preempting, so we know that a chunk of code will run to completion before anything else runs, and that's actually nice because that helps us avoid a lot of nasty race conditions.
Cooperative Concurrency
I was looking around on the web for an image of the DMV to share with you so we can talk about our next subject, and I stumbled on this great article from the LA Times though about an unrelated subject. It does have this great image inside of it of people waiting in line at the DMV, it's a great image because I know what it feels like to be these people, and maybe you do as well. It's no fun to have to wait in line. And if anything is certain in life it seems that when we go to the DMV to get a new driver's license, we have to wait forever. The people working at the DMV never seem to be very happy, nor do the people in line seem to be very happy. In many ways it's a self-fulfilling prophecy, the people in line aren't happy so they're not nice to the people working. The people that are working are not happy so they're not nice to the people that are in line. It's a problem that feeds itself. We want our programs to be happy, and by that I mean we want things to move smoothly and quickly through the one queue we have in our single-threaded JavaScript applications. And for things to move smoothly, that means that we can never have too big of a chunk of work sitting in the queue. Work has to be broken up appropriately, and most importantly we shouldn't block when we perform requests that will have to wait for some response to come back. For example, when we make a network request, say to a website, or if we try and open up a file, both of these requests will take a while to process, we should make sure we do this in a non-blocking fashion, and give control back to the next person in line so that whatever it is they need to be working on can be worked on while our file is being read from disk or our response is being retrieved from the web. In terms of waiting in line, this would be like if we forgot our ID, or some identifying paperwork at the DMV, instead of holding up the whole line while that person that forgot their paperwork goes back out to their car to get it, the next person in line can be serviced while that person is out getting their paperwork. That's what we mean by this. When it comes to JavaScript, the person in control of the line is a bit different than we're used to in life. Even at the DMV, the person working at the DMV can eject you from the line if you're being unruly, can tell you to go the back of the line if you forgot your paperwork, and can decide who they ultimately want to service. Same thing with a teller at the bank, though they're usually nicer, and same thing in many other aspects of life. The person servicing the queue usually has control over the queue. However, in JavaScript that's not the case. In JavaScript it's much more like the bar/bathroom situation. The person that's using the bathroom in a bar has control over the rest of the line. If they want to be reading the paper while they're using the bathroom, well, everybody else will wait. Much like in the program we saw earlier, which I'm showing here again, when we make updates to the display of our web page, changing the background color to red and adding some text, even though we asked that to be done, we were not quite done yet, we had 10 more seconds of work to do, which means that the process to go ahead and make these changes had to sit and wait in that queue until we were done executing all of the things we had to do. In many ways this block here at the end would be like reading the newspaper while there's a bunch of people waiting in line to use the bathroom. Consequently, the process to update the browser has to wait until we're done to be able to update the browser. And then after the 10 seconds, it can make its changes. The fact that the unit of work that's running has control over the rest of the queue leads to another key takeaway about JavaScript, and that's that concurrency is cooperative. And by that I mean, if you think back to the bank/teller example, the customers have to play nice with each other. If one customer wants to hold up the line they can, that's how JavaScript works. Or the people in the bathroom have to play nice with the rest of the people waiting. If there's a line at the bar to go to the bathroom, out of courtesy you probably rush things along while you're using the bathroom. So I think we've hit on the cooperative aspect of this, I hope I've given you enough analogies to be able to think about these when you're working with your JavaScript applications, but we haven't yet really hit on this concurrent part. Way back at the beginning of the course I showed this page here out on the MDN. And it has the word concurrency in the title. And concurrent means that we can execute multiple things at the same time. How is that possible if JavaScript is single threaded? How is that possible with everything we've discussed thus far? Well, just like with human beings, we really can't do two things at the same time, but we can switch tasks pretty quickly. So, if you're out to dinner and somebody texts you, you can be eating dinner, take a brief break, read the text message, and respond to the text message, and then get back to dinner. Even though you don't have the ability to do multiple things at the same time, you can switch tasks pretty quickly. Same thing with JavaScript. As long as the tasks that sit in that queue don't take a long time to process, then it'll seem like we can run things at the same time. We can do something for 10 ms, switch to another thing for 10 ms, switch back to the first thing for 10 ms, and then switch back to another thing. For example, let's go back to eating dinner and responding to a text message. Let's say you took a bite of your food and you felt a vibration go off in your pocket, that vibration would be like queuing something for you to respond to. When you put your fork down you switch contexts, and you pull your phone out of your pocket, you read through the text message, and maybe it's something that you can't quite respond to right away, maybe it's something that requires some time for you to think through things. So you set your cell phone down, you grab your folk, and you have another bite to eat, you think a little bit about how you might want to respond to the text message. For example, maybe somebody told you something rather dramatic and you need some time to think about it. So you think for a little while about it, maybe you grab another bit to eat, you think a little bit more, grab another bite to eat, and now you're ready to respond to the text message, so you pick your cell phone back up, and you respond to it. In a matter of a couple of seconds there you could have switched context several times, that's almost like being able to have multiple things running at the same time. Same thing in JavaScript. Again, as long as our program doesn't abuse and hold up the rest of the queue, our programs will appear to have the ability to execute multiple things at the same time. And by all means I don't want to scare you into worrying about the code you're writing that executes in your JavaScript applications, most of the time you're not doing things that will cause a problem. Even a couple hundred or thousand lines of code that perform simple operations is not a big deal. If you start getting into the millions or billions of operations though, obviously that would be a problem, but modern processors can crank through thousands and millions of operations in a matter of a second. So you can get quite a bit done without really hogging the CPU.
Animated Walkthrough of When Code Executes
Just to make sure you have a good idea of what I mean by cooperative concurrency, and all of these things we've talked about thus far, all of our key takeaways, let's revisit our weather example application, and let's talk through what's going on here in terms of the event loop. I've prepared a slide with an animation that should help you understand how the various different pieces of this weather application are queued up, and how they work together to provide a response that seems like, and indeed actually does, include multiple things happening at the same time. We do indeed fire off two separate requests at the same time, and our processing of the response is so fast that it seems like we get an instantaneous response. Let's take a look at that again. If I change to another city here, and I click Load, bam. So fast I can't tell that things aren't happening at the same time, in terms of processing. Okay, so let's take a look at this code, and I've copied a subset of the code, the most important part for the conversation I'd like to have here. We've got the load function copied over, now I skipped the header of this load function, I skipped these lines here because they're not really pertinent. You know there's work going on to get the city, and to build up the URLs for the request we're going to make, but that's not that important. So let's talk through the hypothetical execution of this program. The JavaScript engine and the tab that we're working in, in our browser, has a separate event loop dedicated just to our application, and this event loop contains a queue, inside of this queue work can be placed that will be eventually executed when whatever is running is complete. In addition to this queue there's a call stack. The call stack contains whatever is executing at this current time. So let's say there's some work on the call stack, there's some code that's executing. We don't know what this is, we don't really care, it just means that right now we can't run anything else. An analogy might be helpful to help you understand a call stack if you're not familiar with what these are. The call stack in many ways is somewhat like a to-do list, when you are performing some task. Let's say you go to the grocery store and you have four or five items on your list, things you need to get from the store. As you look for each item on the list you'll check it off once it's complete. In many ways, as we'll see here, work will be pushed onto the call stack, and then once it's done it will vanish, just like checking something off of a list. And then sometimes when we go to the grocery store we don't have an item, but we might put down a meal that we'd like to make, let's say we want to make hamburgers. Well, if we're going to make hamburgers we know we need buns, we need hamburger meat, maybe we want an onion, and maybe we want to get an avocado, and if we're out of ketchup we might need to get that as well. So sometimes we have things on our to-do list that involve subtasks. In a minute here we'll see how subtasks get pushed on to the stack on top of the work that's executing, much like we might break out a checklist or a to-do list. Okay, enough with the analogy, let's get rolling through this program. So we have our load function, and we know that that's called when we click on this Load button in the browser. So when we click on that button in the browser, if there's some code executing then that button click will get put into the queue. Now depending on the browser implementation, what this looks like will be different, but let's just say, hypothetically, there's some work put on the queue to handle the fact that the button was clicked. Once the code that's executing is complete, then the code to handle the button click can be pushed onto the stack, and can begin to do its work. Now you might be wondering what work does it have to do? Well, over in the index page I used a simple hack to call load when somebody clicks the button. So one of the things that needs to happen here is our load function has to be executed. So the code that handles the button click will push code onto the stack to handle calling our load function. And so now control is passed off to our load event handler. There might be other event handlers that are listening for this button click, but for now our load function has control of the JavaScript engine, and of course, it has quite a few things to do. Again, a call stack is quite a bit like a to-do list. Our load function could be like the fact that we have the word hamburgers on our shopping list, there are multiple ingredients we need to now go look for. Likewise, our load function has multiple things to perform here. And the very first of those things is it's going to call the log function, and pass the value 1. So that call to the log function gets pushed onto the stack. And then in the console we see output that contains the value 1. Log is done with its work so it exits the call stack, and control is returned back to our load function. Now there's a lot that happens here in our load function, right after the log statement, before the next log statement. I've summarized all of this up as, simply the next thing on the list is to send the weatherRequest, and this is the request for the current weather. I'm rolling up five lines of code together, you can imagine I extracted a method for all that insanity. When that weather request is sent off, behind the scenes our browser makes a request to that remote weather API, and then it waits for the response. So somewhere behind the scene, outside of our JavaScript engine, and for lack of space on this slide I'm making these black to indicate that these are happening in some other part of the browser process, so that request will sit there, and now the work to send the weatherRequest is done so it dissolves off of the stack as well, and control goes back to the load function. Now the next thing in the load function is to log the value 2, when that's called then we see 2 come through in the console. Once that's done it's taken off the stack, and control is given back to our load function, and now we have another request to make. We need to send the 5-day forecast request. Again, I've rolled up five lines of code into one here. When this fires of, we fire off another request, and again that other request will also be handled by the browser behind the scenes for us. That browser mechanism, or actually even a Node.js, whatever mechanism behind the scenes is involved in making requests, will listen for the response. Now that we've sent the request our work is done, so sending the request pops off the stack. And then our load function has one more thing to do, it needs to log out the value 3, so we print out 3 in the console. Now let's say while we're printing out 3 to the console, our request for the current weather completes, and the response is now available. Let's also say that that successfully completes. We can't handle that response right now because we have work inside of the call stack, but we can push something to handle that response onto the queue. And that something that we push is the weatherSuccess function that we registered when we set up the request. We'll get to this weatherSuccess function once we've finished everything else that's one the call stack. Okay, so logging is done now, 3 is printed out, so logging is done, control goes back to load, and load is done. Finally control is returned back to the button click handler. Now let's say that while the button click handler is wrapping up, our request for the 5-day forecast comes back, it completes. And let's say that the request failed. Well again, we still have work happening on the call stack so we can't begin to handle the response for the 5-day forecast either, in fact we have something in front of it, the weatherSuccess function, but we can push another chunk of work onto the queue to handle our failed 5- day forecast request. Remember, behind the scenes the browser is handling these requests, so it can have multiple threads handling multiple requests at the same time. So it's possible while our single-threaded JavaScript engine is doing work that behind the scenes other things can be happening, we just don't have those things happening inside of our JavaScript engine. Eventually, the button click handler wraps up its work, pops off the stack, and now our stack is empty, which means we can handle the next thing that's inside of our queue. Our weatherSuccess function is pushed onto the stack, so we begin to execute the weatherSuccess function. Now there are three lines of code here, I'm glossing over this, and suggesting the only thing the weatherSuccess function does is log out some additional information about the current weather response, obviously there's more going on there to update the browser, but let's keep this simple. So we log out the current weather, and then in the console the current weather prints out, let's say the temperature with a value of 50. Obviously the real response has more data, but I think you get the point. Once logging is done that pops off the stack, and the weatherSuccess function is now complete, and so that pops off the stack as well. And now our failure function can execute. Our failure function includes a call to log to the console as well, and it logs out the error that's received. In this case perhaps, we have some error that just says, Oh no. Once the logging is complete, that pops off the stack, and then our failure function is now complete so that pops off the stack as well. And our program is sitting idle at this point, waiting for something else to happen, perhaps for somebody to click a button or do something else.
Mentality: Little Programs
Now that you understand a bit more about how, and specifically when code is executed, I can share a different way of thinking about JavaScript programs that really helps me keep in mind what's going on. We don't explicitly think, at least not very often, about our programs being composed of different parts, but I like to think of a JavaScript program as a bunch of little programs nested inside. It's like our programs are really composed of smaller programs. You could also call these programs tasks if you'd like, operations, whatever you'd like to do to represent the idea that our programs are broken up into different parts that run at different times. For example, we have our load function here, and all of the code here in this load function at the very top is one program, then we have three more programs down below. Now in this case these happen to be nice little buttoned up functions, but they don't have to be this simple. The first program we have down below is this weatherSuccess function. This is a program that's only called if we make the weather request, and it comes back successfully. So that's two programs so far, here's the third little program that this program is composed of. The fiveDaySuccess function is only called if and when we fire off the fiveDayRequest, and it completes successfully. In the animation we just walked through, in that example I had the fiveDayRequest fail, so this program never executed, this part of our code was never executed. Now if we re-run our app maybe things would be successful and things would run, but in this case we never executed this part of our program. This little program went unused. Ah, that's a sad little program. And last we have this failure function, which was used in our animation to handle the failure from the fiveDaySuccess, though this also could handle failure from the current weatherRequest, I wired both of these up to the same failure function. So as we can see this program right here, or at least this part of our program has four little programs inside of it. How did I come up with these? Well, these are the asynchronous seams inside of our application, these happen to be portions of our application that will execute asynchronously. That means, portions of these will be run later on. So I can really only identify these with respect to the top most program, which fires off the requests, and then hooks up asynchronous callbacks. If you can look for these seams of asynchronicity in your own programs, you'll be able to start reasoning better about the asynchronicity of your JavaScript applications. Let me give you a challenge. Here's that nasty little program we looked at before that hogs the CPU. Can you take a look at this code sample here, and can you figure out what little programs we have involved? Go ahead and pause the video if you'd like, and then I'll share the little programs I see when you come back with your own answers. In terms of asynchronicity, I see three programs here. Now there's no right or wrong answer necessarily, because under the hood there's a lot of stuff going on in the browser that we don't understand, and there's differences between browsers. But the three things I see, and I'd expect you to see at least two of these, are first, this whole entire script runs when we click the Run button in Plunker. So let me stop, and let me click Run. Plunker loads up our index.html page, which fetches this script and executes it. And this script, the very first program inside of here, really contains these first two lines of code only, even though it actually contains anything here after as well, but the first two lines are really the heart of the very first little program we have. And this little program finds the button element by ID, and then it adds an event listener, so that when we click on that button, we'll fire off this listener. But that listener, that's not a part of the first little program we're talking about here. As you can see, we haven't changed color of the page, or done anything inside the body here, because we have not yet click on this button. So obviously there's an asynchronous seam right here, this is the second little part of this program. And this will be invoked whenever I click on this button. And again remember this locks up the browser, so right now I'm trying to click on things, and I can't. It only locks up this tab in the browser though. If I come over to another tab I can still do things. So this is the second little program I see. And then I'm considering one last little program, it's not obvious here, but the browser has to repaint the screen once we're done here with our listener. But it's important to think about this as a separate program because that can't run until this listener completes. And that's why this blocking operation here that takes 10 seconds, blocks the browser from updating the UI, and it also blocks us from even interacting with this Browser tab. So I see three little programs involved here. Three separate, asynchronous parts of this program. So an additional takeaway, is that you can start thinking of your JavaScript programs as composed of little programs, and specifically you can find those programs for looking at asynchronous seams, seams where something else is defined that will be executed later on, or maybe not at all. Back to our analogy of people waiting in line for the bathroom, these little programs get queued up like the customers that stand in a line trying to use one bathroom at a bar. Now the big reason it's helpful to think this way, about little programs, is that all these key takeaways above apply to these little programs. JavaScript is single threaded so only one of these little programs can run at a time. There's an event loop that stores a queue, all these little programs have to line up in that queue if they want to run, and these little programs will be pushed into the queue when various events happen, for example clicking a button, or getting the response back from a web request. These little programs had better be non-blocking. In other words, they better be pretty quick. By all means that doesn't mean they have to have a few lines of code, it just means that they probably shouldn't take a couple of seconds, and they probably shouldn't take 400 or 500 ms. If they do need to go that long, you'll need to consider something like running these programs in the background with a web worker. So that's the avoid blocking part of these little programs. And again, you can see in this example over here, this is obviously part of our little program here that doesn't behave so well because it's blocking for 10 seconds. These little programs will run to completion. There's no start one of these and then stop them, and move on to the next. Only one little program runs at a time and it runs to completion, so until it's done nothing else gets to run. Now, there's an interesting consequence for this, because only one of these run at a time, and each of these little programs decide when they start and when they stop, the best we can hope for on JavaScript is cooperative concurrency. In other words, so long as everybody in the line plays nice together, the line will keep moving along, but if there's somebody that's not so nice in the line the line is going to get held up. And again, it's ultimately up to the little program that's running right now to decide when it's done.
Exercises and Examples
Intro
Now that you have the concepts, and some of the basics of understanding how to reason about the execution of code in JavaScript, specifically when code executes, we can now take a look at some more examples to see how this applies in other situations, more complex situations, and I can challenge you to work through these with me to hone your ability to start thinking explicitly about asynchronous seams in your JavaScript applications.
Sources of Async
I find it extremely helpful to think of the different sources of asynchronicity. In other words, the different things that are likely to push work onto the queue in the event loop that will eventually be executed later on. Back in the last module, we went through the animation of work being pushed into the queue. For example, when a user clicks on the button on the page in the sample, the button clicked handler is pushed onto the queue, so this is a source of asynchronicity. Something is now lined up to be executed later on. Later on in the animation when the request for the current weather completed, the weatherSuccess callback was pushed onto the queue to be executed later on. Later on when the request for the 5-day forecast completed, and it failed, the failure callback was pushed on to the queue. So these are some sources of asynchronicity. And we just took a look at the first two of these, user interactions and network I/O. Some other common sources include disk I/O, so reading files from the disk, or writing files to the disk, interprocess communication, for example if you use web workers behind the scenes you could have a separate process where your web worker is running, and as you communicate with your web worker, or it communicates back to you, where you would be the code that's running in your browser tab, as you communicate back and forth that's interprocess communication, which is another source of asynchronicity. Processing that message when it comes back will be pushed onto the queue to happen later on. And timers are another source of asynchronicity, scheduling some work to happen later on. Let's work through some examples of each of these to help you to be better prepared to watch out for these as sources of asynchronicity.
Source: User Interactions
First let's take a look at some user interaction examples. Now we've already seen button clicking, so I'll avoid that. Instead let's take a look at this Plunker I have set up. You can go ahead and copy this link if you'd like to follow along. I embedded everything in the index.html page just so we could stay in one file. So let's walk through this. I have some styles set up, and when we run this sample, you'll see there's a red box and then there are two empty boxes. These are the styles to set that up, so let's collapse those because those aren't important. And then I have the HTML to define this sample here. We have a box that's draggable, and we have two boxes that are droppable, so we can drop things on these empty boxes. First class support for drag and drop was added in HTML5. Now you'll have to check your browser to see if this is supported. Here in Chrome this example will work. Drag and drop is a great example of a multitude of sources of asynchronous behavior, code that will execute later on. And of course, this is all involved with user interaction, because users will be dragging and dropping elements on the page. So we have this red box that we click on, and grab, and drag around. I've defined it as a draggable element, and then I've also defined an event handler for when dragging starts. So when I click and drag, the dragStart event will fire, just like clicking a button on the page. And that event has a handler, this dragStart function, that's defined down below in this endline script. So let's go ahead and pull open the console, clear out the screen here. Each time I click and drag, the dragStart function will be pushed onto the queue, and will then be executed. And this function most notably prints out a message to the log saying dragStart, and it also logs out the event object. So you can see over on the right-hand side here I've started dragging this twice so there are three log entries, if I do it again we'll get another dragStart. So drag and drop is a source of asynchronicity in your applications. This example also has other dimensions of asynchronicity. Pause the video, and see if you can figure out how many different little programs we have involved in this example. Okay, let's walk through this here. I see four little programs. First off we have this whole script block. So I can collapse this, this whole script block has to execute so that we have these functions declared to be wired up to the various different events that can happen. So that's one small program. Then we have three separate events that can happen as a part of dragging and dropping. We have ondragstart, we have ondragover, and we have ondrop. So these each are three other little programs that will execute when each of these events fire. Now we've already seen dragstart here, and we've seen the event that's logged out in the console whenever we start dragging. We also have ondragover, this is the third little program. And you'll see here this is wired up to the dragover function down below. When I click and drag we log dragStart, and then when I drag over the console goes crazy as we log out all the different coordinates as we're dragging over one of these elements that has an event handler wired up for ondragover. And we're logging out the coordinates of where we're dragging that. I'll clear this out so we can run through one more example here to cover the last little program, which is when we drop. When I click this red square, drag it over and drop it, you'll see one last console log entry where we drop the drag element, and you'll also notice the red box is now inside of the first empty box. I can then drag it down below to the other box, I can drag it back and forth. So again, four small little programs in this example. So whenever you have user interactions with your page, look for the various different events that will be firing off as a result of those user interactions, and you'll find asynchronous seams around those event listeners that will be little programs that you can start to reason about as happening later on, and each running to completion before any other work can execute.
Source: Timers
Next I want to talk about timers. Yes, I'm jumping ahead. You've probably used setTimeout or setInterval before to schedule some work to happen later on. On the Node.js side of things, in addition to setTimeout and setInterval, there's also setImmediate, and then there's process.nextTick, so there's a couple of extra ways to schedule work to be done in the future with Node.js. Let's take a look at an example with setTimeout. I have another Plunker here you can pull down. And one key thing I want to point out about the HTML page is that the script is loaded in the head of the HTML page, which means it will start executing before the rest of the body is available. Specifically I want you to note that there is a div tag with an id of content here because we'll be manipulating this in our script. If you hop over to the script.js file, you'll notice at the top I have this line of code that sets the content of that content div, and it injects this text, Main content here from JS. Now, the interesting thing is if you look on the right-hand side you don't see that text anywhere. So why is it that we're not setting the innerHTML on this content div? Let's give ourselves some space here, and let's pull open the developer tools. Okay, and let's go ahead and collapse the File view. All right, if we add a space that will trigger the rerunning of our script, and you'll see we're getting an error over in the console. The error is that we cannot set the property innerHTML of null. So for some reason our document.getElementById is not working. And that again is because, over in the index page, this script is loaded in the head of the HTML document, that means it will start executing before the body is available. And in this case, we've hit a race condition where our content div is not yet available, so we can't find it to set its innerHTML. So one thing we could do, now you wouldn't do this in reality, but what we could do is add some sort of delay and wait for this content div to be available, and we can use setTimeout to do that. So we can come down here and we can call setTimeout, and let's go ahead and stop this so we're not re-executing it, and we can pass a function to setTimeout that will be called later on. This function, let's just pass it, and let's call it in 100 ms. Let's go ahead and yank our line of code and put it inside of that function, so this will get deferred and executed later on. Now let's open the console back up, clear it out, and let's run our script. And now you can see our script is working. We have our main content added in. Now in this case we didn't check to see if the content div was available, that's something you probably want to use if you want to use this in reality. Though, in reality you probably would listen for the page to be loaded, instead of setting your own timeout, and waiting to see if you can access an element. Nonetheless, you can see here, by scheduling some work to be done later on, the body of the page will load, our content div will be available then when this function executes to set the innerHTML on that content div. So when you call setTimeout, when the timer elapses, the 100 ms here, once that time is up, this function that you provide will be pushed onto the queue of the event loop, and then once whatever is running is done, and everything in front of this function in the queue is processed, then this function will kick off. So this highlights an important aspect of setTimeout that's often overlooked. There's no guarantee that our function will be called at exactly 100 ms from now. In 100 ms the function will be pushed onto the queue, but if there's a lot of work in front of it it won't be called until all that work is done. If you want another analogy to think about this, when you put things on your calendar to do on a day in the future, that's like calling setTimeout, and reminding yourself you have something to do in a certain amount of time, a certain number of days from now. That doesn't guarantee that you will get that work done as the very first thing that day, it doesn't even mean you'll get that work done that day at all. You might have a full day ahead of yourself that day, and you may never get to the work that you scheduled.
Timer Delay Gotchas
All right, it's time for a challenge. I've got a Plunker up here that you can pull down. I want you to pull this down, and take a look at it. And the very first thing I want you to do is to figure out how many little programs are involved here. And I'll give you a hint, don't worry about the index.html page, all this example does, the index.html page runs a script, there's nothing really going on with the UI. So go ahead and pause the video, take a look at this, and find how many little programs you think there are. This one's a little bit tricky, there are two little programs here. First off, we have one program for the entire script, there's always one program for the script itself. All these variables you set up here at the top, invoking our testDelay function for the very first time, and actually the execution of this testDelay function for the first pass, that's all one program. It's important to note, the very first time testDelay is called, it is not a separate little program. It's called synchronously as a part of the entire script. Let's talk about the behavior of this program. This program tests the actual delay of setTimeout versus the requested delay. So we ask for a specific delay here, and it's set to 0 right now, we measure the difference between that delay and the actual amount of time it takes before our function is invoked, so this function inside of here. And by the way this is just a plain vanilla function using fat arrow syntax, I'll switch this over just so that it's not confusing. Now when we run this, and because I changed the code here this ran again, a dialog will pop up, and it will tell us what the requested delay was, 0 in this case, versus the actual average delay. And we run this 1000 times right now, so this is the average of 1000 iterations. So you can see the difference is rather interesting. We're asking for 0, but we're actually getting almost 5 ms of delay. Okay, back to counting the number of little programs. So we have one right now for everything, including the first call to testDelay. This testDelay function then on the first call will check to see if we've passed the total number of repetitions we'd like to perform. Right now we're requesting to do 1000 repetitions. And actually as a side note we do one more than the total number of repetitions, we should be starting our repetitions out at 1 if we only want to do the total number. Because we have not yet hit the total number, because we're just starting out, then we continue on with the rest of our program, and this last part is what runs each individual test. We capture the current time, and then we set a timeout, and we specify the delay we'd like, that's our requested delay, maybe it would make even more sense to say requestedDelay here. I'm going to stop the program here. After the requestedDelay expires this function is executed, and the very first thing it does, it takes the date right now, subtracts off the start time, that produces the difference in milliseconds of the actual delay, so we add that to our total actual delay. If you want we could add another variable here, which says actualDelay for the run we're talking about, not the total, and we could extract out a variable here, and add actualDelay. We could do this instead if this makes a little more sense to you, we could calculate the actual delay. By the way, I'm using ES6 syntax here, instead of var I'm using let and const, and actually this could be const. Const is declaring a variable that will not change, let defines a variable that can change. For all intents and purposes just think of const and let as var if you're not familiar with those. So we add each run's actualDelay to the totalActualDelay, and then we call the testDelay function again. So this is a recursive function in a way, because after the timeout is elapsed, this function is then called again to perform the same process. So our second program is this little function right here. But it's really important to note that this second little program also contains this testDelay function as well. It synchronously makes a call to testDelay, just like our first little program does, so testDelay is a part of both of our little programs. It's kind of interesting to bend your mind that way. And thus we have two little programs in this example. Let me finish explaining this, just in case any of this example is confusing to you. So testDelay will be called after the delay, and after we've computed the actualDelay, and it will run this whole process again, and it'll keep doing this until we hit the total number of repetitions, in which case then we print out the Actual Average Delay. And the Actual Average Delay is computed by taking the totalActualDelay, and dividing by the total number of repetitions. So there are two little programs in this sample. That's important, but also important is that when you request a delay of 0 ms, you're really looking at about 4 or 5 ms, at least with this sample program. Again, we're looking at 4.6 actual average milliseconds before whatever function we pass is invoked. And if I crank this up, let's say let's change this to 10, don't make this too high or you will permanently lock up your browser window because we're running this 1000 times, but 10 ms times 1000, you can see in this case we requested 10 and our actual average delay was about 10.744. So this was much closer. Now the reason for this, is that when you say you want 0 ms, the browser is putting in 4. So if you put 4 in here instead, you'll see we requested 4 and we got about 4.6. Anything less than 4, say 2, will still come out at about 4.6, because the browser is substituting 4 whenever the value is less than 4. So be aware of that, but more importantly be aware that there's still a fractional component that's rather substantial here. And if you had a lot of work in your queue this number could balloon up exponentially. So there's one more new key takeaway here, and that's that the timer delay is not a guarantee that the function that you pass will be invoked right at that amount of time. And again, this is because a timer is just the amount of time that elapses before an item is pushed onto the queue. It still has to flow through the queue and compete with whatever's in there first, before the code will actually execute. So the delay in a way is a minimum.
Not Always Async
Okay, time for a challenge. Take a look at this sample of code, and pause the video to figure out how many little programs are involved, and then once you figure out how many little programs are involved, use that to figure out what will print out in the console. Okay, let's see what happens here. I've opened up the console, and cleared it. Let's go ahead and hit the Run button. And there's the output, is that what you expected? From where you're sitting right now, there's no way that you could have known that that's the output. So if you got it right, well guess what, no you didn't. And if you got it wrong, well guess what, no you didn't. There's no way to be right or wrong about this example, because I didn't show you what's inside this forEach function. There are many, many different ways I could have written this function that would change how the output displays. But, I'm really hoping you guessed that something else would show, aside from what actually displayed, and I'm hoping that because I primed you to think that something else would show. So that's a good thing, and it's okay because that's really what I intended. Hopefully somebody falls into the trap I kind of laid out here. We've been talking about callbacks, and we've been talking about things happening later in this course. I set up this example so we have an array of numbers, we log out start, we call this forEach, and then we log out end. And inside this forEach, I set this up to hopefully make it look like this was a callback. I even named the parameter to forEach callback, and I was hoping that you would think this would execute for each of the numbers later on, but in fact, if we open this up, all we're doing is looping over the numbers, and calling the callback with each item. And this is being done synchronously, and that's why we have start, 2, 4, 6, and then end. This is all one little program. And there's no way for you to have known if it was one, two, or many, many more without seeing this forEach implementation. And this is really, really important. And so it's time to pull up our slides and talk about another key takeaway. And that key takeaway is that just because something looks like it's asynchronous, just because there's even a parameter called callback, that doesn't mean that it's asynchronous, it may indeed be synchronous like we saw right here in this example. A good way to remember this is even if it looks like a duck, it quacks like a duck, and it waddles like a duck, that doesn't mean it's actually a duck. So be very careful when you're reading through code, especially if you're using somebody else's code, or a third party library, be careful to validate that what you think is asynchronous actually is. If we come back to the example here, if I had put a setTimeout inside of here, actually let's put the setTimeout around the callback for each item, create a function, and call the callback inside here, now we legitimately have a situation where things are executing later on, and you can see over in the output here, 2, 4, and 6 come after end. And of course if I fold this down, and if I were to have started this clip right here, things would have been completely different. And that's why I'm telling you, without seeing the forEach there's no way to have been right or wrong about the number of programs, and what would show in the console when we run this. So make sure you verify your assumptions when you're looking for the little programs in your application, especially when third-party code is involved.
Debugging Assumptions
Let's get back to our sources of asynchronicity. The next one on the list is network I/O. We've already covered this a little bit, so what I want to do is a bit different. I want to take that existing example we've already been talking about, and I want to show you how you can debug your program, and how that can help you figure out where your little programs are at, where your asynchronous seams are at. So what we can do here is just at the top of this script we can just type debugger. And that will stop our program execution right on this line. We'll have to open up the developer tools to do this. If we change our program then it will run again, and now we'll hit the debugger. You can see Chrome says we're paused in the debugger, let me pull this over a little bit, and make this a bit bigger, and you can see the line we've stopped on here. And of course, I can't get into all the nuisances of the Chrome debugger, but like any other debugger in the world, you have a bunch of information that's captured about the current context of your program, the current execution context. You can see Threads, you can see the Call Stack like we talked about earlier, Breakpoints, and various other breakpoints you can set to help you out. On the right hand side you can see the scope information, and you can add watches. Right now, the Call Stack says, we're in an anonymous function in index.js. We can now come up to these little arrows, and we can click on the one, or hit F11, to step into the next function call, or we can step over the next function call. So I'll use the step over, F10. We'll step over hardcoding the value of this text box, and watch that happen by the way. And then we'll call the load function, except I stepped over that, so I needed to step into that. So let's just resume the execution of our program, and start this over. Okay, let's start again, let's step over setting the city value, but let's step into the load function. Watch the Call Stack here as I step into the load function. Now you can see that the load function is pushed onto the Call Stack. This is just like the call stack I had in my animation earlier. So this is the real live call stack of your application. So you can see at this point we're still in this anonymous function for our entire script, so we're still in the little program that represents the whole script file. Now in this case we can step over getting the city value, we can step over showing the showResults of loading, and we can step over creating our URLs. And so here we are on this console.log call, so if you're wondering where and when does this execute, well, it's a part of the load function, which is a part of the little program we have that represents our entire script. So this cis how you can verify your assumptions. Step over that and we print out the log value of 1, if we come to the console we can see that. Go back to Sources tab, we can continue on debugging. And here we are setting up our weatherRequest, so if you wondered where that happens at, well, let's go ahead and step over these lines of code, and here we are in our next log statement. So we fired off that web request, if you go to the console there's nothing printed out though, so that's not complete, and no handler is fired off. We can now step over logging out 2, in the console we have 2 now, step over setting up our second request. Again, in the console though, nothing's come back for the weatherRequest, this is the previous call. So we have 1 and 2 right now. Step over, we should have 3 now, and there's 3. Okay, and you can see we're at the end of our load function here. So our load function is now done. Let's go ahead and set some breakpoints in here. Looks like I was having a little trouble setting those breakpoints. We've got one now set on line 37, get one on line 43 as well, and get one on line 49. Now we can resume the execution of our program. Boom, we hit one of the breakpoints in our weatherSuccess function. So we had a successful request, in this case. And if we look at the call stack now, there's no anonymous function down here for our index.js file. That means we are in another different little program right now. Now I will say right now, the way we're debugging is not so helpful, it would be nice to have some information about what little program we're in, but because we don't have that anonymous function like we did before we know we're in a different little program, and right now the root of this call stack is weatherSuccess. So this weatherSuccess function is actually what was pushed into the queue. We can step over here, and that means we will have printed out to the console, and will now show the weather here in the browser. So there's our current weather object in the console. And if we resume the execution of our program, boom, we hit the fiveDaySuccess for our second request. Again, if you look at the call stack, no anonymous function here, so we are in another little different program. And fiveDaySuccess is the root of that call stack. So that's clearly starting to seem different from our weatherSuccess function's little program, so this is our third little program. And we can step over the lines of code here, we'll have our 5-day forecast in the browser, and in the console you can see the five day forecast object. And if we resume the execution of our program, it looks like we never hit the failure function. So if you're ever wondering about the little programs in your program, you can use debugging, and actually look at the call stack to verify your assumptions. In the next clip I'll show you an even more helpful feature of debugging, which is asynchronous debugging.
Debugging with Async Call Stacks
Okay, one more helpful thing when it comes to debugging in the browser. If you're trying to validate your asynchronous assumptions you should check this box for Async debugging. Let me show you what this does. I've kept my existing breakpoints, and I've cleared out the console. Let's hit the Load button again to run through our program. Now because I hit the Load button we jump to the fiveDaySuccess function, it looks like the five day request came back first. Now here's what's really interesting when you check this Async box, look at the call stack now. In the last clip it was just fiveDaySuccess, now you see additional information, you see onclick, you see load, and you see XMLHttpRequest.send, and it says async. So this fiveDaySuccess function was called as a result of sending a web request, an XHR here, and that was called because of the onclick event calling our load function, which then called the send function. So this shows you where your asynchronous calls came from. This shows you where things that run later were triggered from, it's really neat. So this can show you a different dimension to our little program. In fact because I triggered this because of clicking the Load button we see a different little program than we see when we first load the page. So right now we have onclick at the base here, let's resume the execution and just skip over everything else here, and let's just reload the page instead. So let's make a change here. You see we hit our debugger now. Still at the breakpoints, let's go ahead and resume execution, we hit a breakpoint. Now this time because we reloaded our script, we don't have onclick here because we didn't click the button, this is our anonymous function, so our little program that represents our script. So this is a different dimension of a little program. As you can see now, because of the examples we've gone over, little programs can overlap with each other. We saw this back when we were estimating the actual delay versus requested delay of the setTimeout function, now we're seeing this again here. We have overlap and what can call this load function, and that makes sense because different parts of our program can call the same functions we provide from different sources. In this case the source is loading the script versus the source being clicking a button. Anyways, even in this case we can see the same call stack here, originated from this original call to load, going through sending the request, and then that successful sending of the request triggers the weatherSuccess function. Now notice this time, the current weather request completed first, so that's the first callback we hit. And then we resume execution, now we hit the success on the five day, and notice it likewise shows the original location of this, the origination of this. And you'll notice the load function is referenced on line 31, over here line 31 here is fiveDayRequest.send. In fact you can click on these different parts and jump right to those lines of code. Here's the call to load, here's the call to send, and then here's the fiveDaySuccess function. So you can click through this almost as if this were a synchronous call stack, pretty neat. Again, the Async checkbox for that.
Source: Disk
Next up, disk I/O, and for this example we'll take a look at Node.js. This example uses the fs module that's a part of the Node.js API. I'm going to stop right here, and just ask you to take a look at this and tell me how many little programs are there? There are two little programs in this example. One for the entire script, and then we have a callback here, and that's the second part of our program. Notice this is using a special convention, just in case you're not aware of it. It's typical in Node.js for a callback to receive two objects, one the error, and one whatever the result is of the function we've called, so readFile in this case, it's the file contents. That callback also comes as the last argument to a function. Callbacks are a notorious sign that a function is asynchronous in Node.js, but just like always you can't assume that, you have to look at the implementation of the function, or look at some good documentation about it to know if in fact it is asynchronous. The Node APIs themselves, the built-in ones like the fs API, those you can all trust to be asynchronous. Now of course no trickery here with the logs, they're all inside of our second little program, but let's go ahead and run this anyways. You'll see we actually get an error back. I left this deceptively, so we could talk about errors. In this case we get an error so we abandon the processing in our callback. This is a lot like try catches in a lot of other languages. Though, with asynchronous functions when things go wrong that error has to be passed back somehow, either to a dedicated callback like we saw with the web request there's a failure callback, or in the case of Node.js it's typical for a callback to receive either an error object or an undefined parameter for the error object, and then the callback also receives the response, or the result, if things are successful. If you are curious about making this work, we can refer to the directory that this script is in with __dirname, and this will fix the problem of pathing to the mysql file. When we run this program now, we actually have the program printed out here from this mysql file.
Nodejs nextTick and setImmediate
While we are on the subject of Node.js I want to take a minute and talk about setTimeout, specifically setImmediate and process.nextTick in Node.js, which are some additional means to schedule work later on. By all means you should refer to the Node.js documentation as you learn about setImmediate and process.nextTick when you want to use it, but I do want to briefly point out that there is good documentation for it, here's the docs for setImmediate, and then under the process module you'll find the docs for process.nextTick. These are additional means of, more or less, triggering work to happen later on. So in both of these cases these are like timers. First let's take a look at process.nextTick. Let's say we set a timeout to call a function here in 0 ms, which will default to 1 ms in Node.js. And then in 1 ms, when this function is invoked, we will log out st-1. But let's also do this, let's call process.nextTick, and to process.nextTick we just pass a function. And in this one let's log out, and we'll call this nt, for nextTick, -1. Now before I run this, tell me what you think is going to happen, no wrong answers here, just think about what you think will happen here. And by the way, use your understanding of the little programs here to take a stab at this. Okay, I've opened up the terminal over on the right-hand side here, let's go ahead and run this program. So I call node and pass set.js. Interesting, right? Is this what you expected to happen? Maybe to make this a little more interesting let's make a copy of setTimeout, paste it here, also waiting for 1 ms, and one after we call process.nextTick. So this will be st-2, and this will be st-3. Let's run the program again. Before I do that though, what do you think will happen in the output? Again it looks like the nt, the nextTick, is beating out all of our setTimeouts. So it's obvious that process.nextTick is happening before all of our setTimeouts fire off. Could it be that process.nextTick is synchronous? Well, if we wanted to know that we could debug our program, or we could go ahead and log out, make a new Error, and grab the stack. Let's run this program again, make a little more room here. Actually let's clear that out and run that again so we get the full stack trace. So if we look at a stack trace here, and maybe it would be helpful to compare it to another stack trace, let's put one of the stack traces on the setTimeouts. Clear again, and run again. All right, so we have the stack trace for the nextTick here, and then the stack trace for the last setTimeout. You can tell these are different. If we put a stack trace in for the root of our script here, let's run this actually, so we have three stack traces now to look at, hopefully this isn't too confusing. So the first stack trace is for the root of our script. You can see, at the very top here we're referring to set.js on line 19. So that's where we're at right here. Then we get this stack trace for our nextTick. Ah, and these look somewhat different. If we look at our first stack trace you can see we've loaded up the module for set.js, but this is in a call stack involving this module.js file, whereas our nextTick, well, there's module.js, but then there's this next_tick call stack. So this is happening out of band with our scripts. Now behind the scenes this could be synchronous from this runMain function, but it's at least not synchronous in terms of this script. If we scroll down here, and look at the call stack for the timeout it's completely different. There's this timer.js file referenced. So we do indeed have asynchronous code being fired off with both nextTick and setTimeout, it just looks like nextTick has the upper hand. Let's comment out these call stacks, clear the screen here, and run our program again. You can see the nextTick is winning. Let's copy and paste that actually, call this nt-2, and let's see if we have 2 of these, let's see what happens now. And it looks like the nextTicks fire off in the order that they're registered. If you want to understand how nextTick works, again the documentation is the best place for that. I'll point out a few things here. First off, nextTick, all the callbacks that you register will be run at the end of the current event loop turn. That means, whatever is running right now, which is our script at this point to register all these things, once that's done, all of the things we registered with nextTick will be called, all of them. And actually that last part you have to scroll way down to the bottom to find. Note: the nextTick queue is completely drained on each pass of the event loop before additional I/O is processed. Keep that in mind. Basically, you're putting things at the front of the line. So you could think of a bathroom at a bar, or maybe the line outside of a bar, nextTick is like the bouncer that lets people cut in line, that lets the VIP people get in ahead of everybody else. The other construct in Node.js, setImmediate, that's new. All I want to say about this is just like process.nextTick, it's for running something in the future, but sooner rather than later. It's quite a bit like calling setTimeout with no delay, though technically that's not accurate for all intents and purposes, for you that's all you need to know. Also, one of the things you have as a guarantee of process.nextTick and setImmediate, all of the items that you schedule with these will be scheduled in order, so you'll have some guarantee about the order of execution, which is something you don't always have with timers. I just wanted to point these out to help you understand that there are other means of scheduling work to be run later on with Node.js, which is something I think you should be aware of, but unless you are writing some library that requires a particular order of execution in something really, really special, you'll probably stay away from process.nextTick. And the only situation you would really need setImmediate is if you want something to happen with pretty much no delay, and you want to guarantee some ordering of the various immediate callbacks you would schedule. So all of the things you call to setImmediate would be ordered, but don't try and come up with a situation where you have setTimeout and setImmediate interspersed and expect to see any type of guarantee about ordering there, you just won't have it.
Web Workers
Next up, let's talk about IPC as another source of asynchronicity, which is the last one because I've already covered timers out of order, because I like to do that. I have a really neat example to share here. Go ahead and pull up this Plunker. And then you'll need to make a change to it. Make sure you've stopped it if it's running by default, or if you ran it, and change the script from background.js to inline.js. There are two different examples here, and we have to swap out the script to see each of them. I want to start with inline.js. So we have an index.html file here, we have inline.js, and we have style.css. We're going to focus on these three files first, ignore the first two, background and crunchNumbers, ignore those for right now. Let's talk about this index.html page. We include the stylesheet, which is actually important in this example, you'll see that in a minute. Then we have a progress element, this is an HTML5 element to display progress. And the reason we wanted this style.css sheet is because I have styles set up to style that progress bar, so it's not tiny and so you can see it here, as I'll demonstrate in a minute. And then last we import this inline.js script, which we can take a look at. Not too complicated, but I'm trying to simulate something here. I'm trying to simulate grabbing that progress element, and then having a function here that updates the UI to show the progress in that progress element. So we've got this function here, which we could refer to as showProgress, we're passing that showProgress function to this crunchNumbers function. This crunchNumbers, let's say this is going to perform some really intense calculations. What I do here in this crunchNumbers, I iterate through a loop 11 times, and at the start of the loop I report the progress, and then I go through this intensive calculation that blocks for a second. Then we loop back, we update the progress again, and then we wait another second. So this crunchNumbers is an intensive operation that takes 10 seconds, but in the meantime it's trying to update the progress. So my first question for you, how many little programs do we have here? What do you think will happen when we run this? Okay, let's go ahead and run this and see what happens. Click the Run button, and everything's locked up. I can't touch anything here. And that makes sense because we have this loop here that's blocking. And then the UI shows up about 10 seconds later with the progress bar filled. So that's not very helpful to us. We're not getting the progress back as things are happening. And of course that's because this crunchNumbers function has to run to completion before the next thing can run in the browser to be able to update the UI. We've seen this pattern before when we were setting the background color to red. Now one things that's really interesting, if we add a console.log in here, and we log out the step, of course it would help if I stop the program so I don't run it again and lock it up, notice in this case we do actually get the progress updated to 0. Okay, I've stopped the program. If we console.log the step, pull open the console, clear this out, I'm going to collapse the File view so we have some more space here, get about a third, a third, a third. Let's go ahead and run this now. Interestingly our UI is not updating, but we are getting values out in the console. And actually interesting is the UI did show with 0% to begin with, and then it shows with full, but what I find fascinating is that the console does show us our step values, but that's not going to be helpful to everyday users. So it's an interesting side effect of the implementation here in Chrome at least, that logging to the console isn't blocked, but updating the UI is. Let's go ahead and show some more of our code here. So the reason I put the style on here is because this bar is now big for you to see. What I want to do is rewrite this since we have number crunching going on, so the number crunching is happening in a web worker. Because we know when we have CPU intensive work to be done, we don't want that running and locking up our browser when we can use web workers to get around that. So, go ahead and stop the application, go over to the index.html, and change inline to the background.js script. And then let's go ahead and take a look at that background.js script. In here we do the same thing, we grab the progress element, but then we do something different, we create a new Worker, this will create a new web worker, we pass the script that we'd like it to run here as a string, which is this crunchNumbers.js script, we'll look at that in a minute. So this will launch a worker, and then we can listen for messages. So we attach to the onmessage event, so when the worker sends us a message this function will be executed. Does that sound familiar? Yep, so this is a little program inside of this example. The worker will send us progress updates through this onmessage function. So we'll take the message, grab the data off of it, and use that to update the progress element. Before we take a look at the crunchNumbers though, let's just go ahead and run this. And there you go, as you can see the progress is updating 10% every second that ticks by. That's what we wanted to see in the last example. So this is what we get when we put our intensive work in the background, and then send updates into our primary application. So we have this web worker here that's running this crunchNumbers.js script, so when this runs, crunchNumbers is called, postMessage is the function, which is used to send messages back to the primary JavaScript engine that controls the UI of the Browser tab. So we can send messages back, more or less, to our bachground.js script here, which can then update our UI. The web worker can't update the UI, it doesn't have access to that. And although I said that this is an example of IPC, which would be inner-process communication, it's actually not. This web worker that we've created, using new Worker, this is what's called a dedicated web worker, it just runs on another thread inside the same process, but we still do have some isolation between the JavaScript execution engines that are controlling each of these workers, if you will. We have the ones that's involved with the Browser tab itself, and then we have this web worker in the background. So there is some asynchronicity happening here via separate threads, but it could also be a separate process. And in fact if you use what's called a shared worker, those actually run their own process. Nonetheless, this background worker can't update the UI directly because it doesn't have access to the DOM to update that progress element. So we have to send the message back by calling this postMessage function, and we pass back the step. By the way, I'm just passing this in as a callback, like I did in the prior example. We could just access this right here, and that might be a little bit easier to understand. Every time this steps through, even though this blocks right here, we send a message back to our other JavaScript engine, and that message has the new value to update the progress indicator. And that's why we get the updates here, because we're not blocking our primary thread that controls rendering. So this is a rather interesting example because I really think this highlights the benefits of JavaScript. It's single threaded, but hey, we can spawn other threads that can't work with the same state, they can't share memory together, they can't work on the DOM together, but they can each do their own thing, and they can easily pass messages back and forth. And via the first class asynchronicity of JavaScript, because that's something we're so used to, it's not so weird to work with web workers. The idea of passing messages back and forth is pretty simple and straightforward. So we can even have concurrency in JavaScript with multiple engines running, and we know within any one engine things are pretty safe, race conditions are pretty much nonexistent, but race conditions are not entirely nonexistent, and we'll turn to that next.
Race Conditions
We are back in the example that we spent quite a bit of time with in this course of fetching the current weather and the 5-day forecast. Maybe you've noticed this, in fact we did see this earlier in this course, I didn't say anything about it at the time, but there's a slight bug with this code. Take a minute and look through this and see if you can explain why, and I got this to happen already, and I'll show you how to make it happen, see if you can explain why the 5-day forecast didn't show here, nor did any error message show. I'll give you one hint, it's somewhere down here in the callback functions. Take a look at those and see if you can find a problem with those callback functions. Okay, first things first, let's replicate this. So I'm going to pull open the console, and it's cleared out right now. Split the screen just a tiny bit here. And I'll click the Load button, and boom, actually it happened right away, it's pretty awesome, got lucky. So in the console we have 1, 2, 3, five day, and current weather. If I run this again though, the five day is showing. In the case we have 1, 2, 3, current weather, five day. So in this very first case I got lucky here, the five day came back first. Notice that time it looks like current weather comes back first, probably because we make that request first, maybe because it's a faster request, I don't know, but every once in a while, I think, I'm not getting very lucky here, and hopefully I don't hit my API quota. Well, I don't think I'm going to get lucky here, but if we scroll way up to the top every once in a while we can see the five day completes its callback first, which means that request came back first, and got pushed into the queue ahead of processing the current weather success. And that's why we then don't see the 5-day forecast because the weatherSuccess calls showResults, which just overwrites with the equal sign here, the innerHTML of this div, this results div. However, fiveDaySuccess calls append, which +=, so adds to whatever's in there already. So weatherSuccess is overriding the fiveDaySuccess in that case. This is a race condition. Even in JavaScript where it's single threaded, and we have cooperative concurrency, race conditions even though they're pretty rare, can still happen because the order of the items in that queue is not guaranteed, especially when we're dealing with asynchronicity of handling web requests. We don't know which one's going to complete. Most of the time the first one we fire off, if they're both about the same amount of time, of course the first one we fire off is going to come back first most of the time, but that's not always the case, so I just want to make you aware that you can still have race conditions in terms of the order of the operations inside of the event loops queue. So this is a key takeaway. Race conditions are not nonexistent, they're pretty unlikely because most of the time the order of things in the queue doesn't matter, but hey, if you're going to do things in parallel like make two web requests, if the order of processing those matters, then you need to do things safely. For example, we could have both of these call appendResults. Now the only issue we'd have is they might be flipped around, though that's something bearable. And we also have loading here, we probably want to change that. Write some logic, if loading is in there, then go ahead and call showResults. At least this is safe to show us the data. In a real app you'd probably want to decide what shows first, or maybe have two separate divs that you write into instead of one result div that you write everything into.
Event Listeners Are Synchronous
I've got another challenge for you. Take a look at this code sample here. I'll save this so you have this available. Go ahead and download this Plunker if you'd like. Tell me, how many little programs do we have here, and, when I run this program, what will show in the console? Okay, let's go ahead and open up the console, clear this out. Are you ready? Let's run this program and see what happens. Did you expect that to happen? Interesting, right? We grab the button element, we add two listeners for the click event, print out click-1 and click-2 when that happens. It seems pretty reasonable that click-1 would come before click-2, but here's what's really interesting, we log out pre-click, we call click on the button, and then we log out post-click. There are two little programs here, not three or four. It might be tempting to think that there could be four little programs here, that these eventListeners would be separate programs that are fired off asynchronously, they're not. EventListeners are fired off synchronously. So what we have here, we log out pre-click, we call btn.click to simulate a click, which emits the click event, and that click event synchronously calls each of the handlers, that's why in between pre-click and post-click we have the click log messages. Once that's done firing off the event synchronously, then we can print out the last message here. So there are two little programs, the whole script, which wires up the event handlers and sets this timeout, and then there's a separate little program here that performs the logging and emits the event, which then synchronously calls the two listeners for that event. This seems kind of weird and you might be like, well how do I ever know? Well don't forget, debugging can be your friend. We can come in here and add a debugger statement. The debugger will open up here, and you can see we're right on this line for the first click event listener. We can add a breakpoint to the second listener, but we don't really need to do that because even with the first one we can take a look at the call stack, and we can see what we were interested in here. Take a look at this call stack, read through this and tell me if this does or doesn't confirm that the event listeners are fired off synchronously. Okay, so let's walk through this. At the very bottom of the call stack, and I have async call stacks turned on, we have script.js line 12, click on that here we can pull that up in the debugger, that's the setTimeout line. So that's the origination of this asynchronous call stack, and hence the setTimeout here with async in parenthesis. So the real call stack is this part right here, which is script.js line 14 emitting the button click event, and then you can see on top of that in the call stack is line 4, which is the debugger, which is where we're at right now. If I step over that into the next line, you'll see we're at line 5 here, we're still in that same call stack, so we have a synchronous call here. Line 14, call.click, and then pushed onto the stack is our listener. If I resume execution I'll hit the next breakpoint, and you can see here again, line 14 is at the very root of this call stack, and now line 9 is the current frame on the call stack, which is logging out click-2. Hop over to the console you'll see right now we have pre-click and click-1, if we resume the execution of our program, we now have click-2 and post-click. So event listeners fire off synchronously, keep that in mind. Don't confuse those as little programs, or asynchronous seams, they're not. If you wanted to perform something asynchronously as a result of an event, wrap it in a call to setTimeout inside of your event listener, so you could call setTimeout instead here, and then it would indeed be asynchronous. Now you might be wondering, why in the world would event listeners be synchronously called? Well it turns out, remember we can have race conditions with the order of operations in the queue, if something is asynchronous we don't have many constraints that we can rely upon to guarantee the order of execution, and thus some really bad things could happen. For example, here's a Node.js chunk of sample code that makes a web request. The response comes back as an event emitter, a source of events, the key I want to point out is if events aren't fired synchronously, and are fired asynchronously instead, then the data that comes back as a series of data events, there'd be no way to guarantee the order of that, and then a web page could get all jumbled up. This data event will fire multiple times, and if this weren't synchronous we couldn't guarantee that those asynchronous firings would then be handled in the correct order, and thus we wouldn't be able to know what the correct order is in our code. So event listeners have to be synchronous to ensure the order of events are preserved. Now this will sound really weird, but there can be asynchronicity between an event and another event of the same type being raised, but all the listeners for an event have to be fired off synchronously, so that we know they complete before we might be firing off another iteration of the same event.
What's Next?
I hope you've enjoyed this course, and I hope you've learned a lot in the process. Don't forget we have this key takeaways document that wraps up all the main points of this course to help you start reasoning explicitly about asynchronicity in your JavaScript applications. Keep in mind, JavaScript apps are single threaded. It's a lot like having a bank with only one teller, but I think the analogy of a bar with only one bathroom is better, and that's because the person in line gets to control the bathroom, the person going to the bathroom has control of things. JavaScript has this concept of an event loop. The key takeaway here is that there is a queue that forms then of the people that would like to use the bathroom. Only one person can use the bathroom at a time so a line forms with everybody else waiting. JavaScript is designed with this idea of having non-blocking operations. So for example, making a network request or reading a file off the disk, those are not blocking, so when you make one of those requests you can then work on other things, work on other things in the queue. Once a request is complete, and the response comes back, then a function to handle the response will be pushed into the queue. So that's how we can have a program that doesn't lock up, because we don't wait for operations that will take a while to complete. However, that doesn't mean that you just don't have to worry about this when you're writing your own programs because you can end up blocking the queue. This is like somebody that's hogging the bathroom. There's a long line and somebody decides to read the newspaper, well, everybody else is not going to be happy. The person in the bathroom gets to decide when they're done, not the people outside, not somebody running the bar, the person inside the bathroom gets to decide when they're done. That's what the idea of running to completion represents. The chunk of code that's currently executing, only one can execute at a time, the chunk of code that's executing gets to decide when it's done. Therefore it's very important that the various different pieces of code in your application play nice together. It's like having a line of nice customers that respect each other, and don't hold up the line, if there's a long line they don't ask frivolous questions of the bank teller. They're not reading the newspaper on the toilet. And the key idea around all this, if you want to start to understand asynchronicity in your programs, is to start to look for the little programs that make up the people in the queue. So look for the slices and dices of your program that represent customers waiting in a line, the things that will execute later on in your application. Look for these asynchronous seams, and start to think explicitly about them. Don't forget that this notion of a timer, to set a timer and execute some code later on, the delay is not a guaranteed amount of time, it's a minimum. After the timer expires whatever function you pass to be called later on will be put into the queue, so if the queue's full at that point it still won't get processed. Just like when you put something on your calendar, that doesn't mean you'll do it first thing on that day. And most importantly, don't make the assumption that something will by asynchronous when it's not actually asynchronous. So just because a function you're calling takes a function, that doesn't mean that the function you pass will be called later on, it could be called synchronously. Just because it looks like a duck, sounds like a duck, and walks like a duck, does not mean it's a duck. And also, just because JavaScript is single threaded doesn't mean you can't have race conditions. The nice thing is you don't have a race condition within the context of a little program executing inside of your program. Because of the run to completion semantics, you don't have preempting where something can stop the execution of your code at some random location, but things can come back in a random order, for example, with web requests that fire off, and you need to make sure that those are synchronized if they need to be. The only thing I'll leave you with is I'd encourage you to look out for the next course that I'm creating. This course is a precursor to the next course that I want to do. You could stop here if you want and walk away with this great ability to reason about your asynchronous programs, but if you would like to be able to write asynchronous programs in a fashion that's maintainable, that doesn't deal with the insanity of callbacks nested inside of callbacks nested inside of callbacks. If you'd like to learn about promises, and see how generators and promises can give you a new style of writing asynchronous code in a synchronous fashion, well then join me for my next course, keep an eye out for it in the library. In that course I'll show you how to write asynchronous code that's a joy to maintain, and not a thorn in your side.
Course author
Wes Higbee
Wes Higbee is passionate about helping companies achieve remarkable results with technology and software. He’s had extensive experience developing software and working with teams to improve how...
Course info
LevelIntermediate
Rating
(154)
My rating
Duration2h 5m
Released2 Jul 2016
Share course