Rethinking Asynchronous JavaScript

Exercise 1 Solution

Kyle Simpson

Kyle Simpson

You Don't Know JS
Rethinking Asynchronous JavaScript

Check out a free preview of the full Rethinking Asynchronous JavaScript course

The "Exercise 1 Solution" Lesson is part of the full, Rethinking Asynchronous JavaScript course featured in this preview video. Here's what you'd learn in this lesson:

Kyle walks through the solution to exercise 1.


Transcript from the "Exercise 1 Solution" Lesson

>> [MUSIC]

>> Kyle: Let's talk through how we might choose to solve this problem. When this callback is called we're going to get a response back and because of closure we gonna know which one of the three files it came from. Does everybody see that? So what I could do is I could have a handle response function that I call out to with the file name and the text that it got, okay?

Now I could do that code directly inside of here but I'm pushing it out to another function so that I have a little bit less nesting going on. All right now, handle response receives a file name and some contents for the file, everyone following that? And what I'm going to need is something in closing scope, in this case probably the global scope, some variable, some object that is going to keep track of the responses that have come back, in case I get a response back earlier than I'm able to render it.

Everybody with me on that? I might get a response back and be able to immediately render it but I'm going to need something to hold on to the responses that I get back that I can't yet render. And I'm going to need to be able to, basically, iterate over that collection of responses when it is time to print things out and print out whatever I've gotten.

So the way I'm going to do that is simply have a variable called responses, and we'll make it an object. This is not, by any means, the only way to solve these exercises. As with almost everything we do in these workshops, there's many different ways to slice it.

I'm only walking you through one way of modeling this mentally, okay? So handle response could say if responses of filename. That is an object whose property is the filename, the URL that was used if that's not already set. So maybe I would do that instead of response as a filename, maybe I'll say if not filename in responses.

So I make sure for a fact that this function is getting called with a response that I haven't yet seen. This takes care of maybe the function somehow getting double called or whatever, right? So I'm gonna say this is a response that I have not yet seen yet.

So the first step is definitely to add it to my object. I'm storing it into an object, okay?
>> Kyle: File name being the property location on the object, contents being the value that I'm putting into the object. So if that was file1, if this was the first time this is being called, and that was file1, I could iterate over the stuff in responses.

And if there was stuff to render, I could immediately render it. Take it right back out of the response's object. Or I might have just put file two in and file one isn't yet there in which case I can't go on yet I just need to leave it there and try it again later, okay?

Keep in mind this handle responsibility called all three times. So we called every single time we get an AJAX response back is going to get called so it's going to have an opportunity to be called for all three. But we're not sure which order there's six different ways that this can happen or three different ways or whatever.

There's a lot of different ways this can happen and we're not sure which order is going to happen and so we are trying to coordinate that or normalizing that concurrency. So after I've made sure that my response has been stored into my responses in the contents have been stored in the responses.

Now I could simply start out with an array. Now, this is a simple cheat way of doing it. There are, of course other ways where I could've kept those in a external location in some way but I'm just trying to come up with the simplest way of solving this problem.

If I know what those URLs are, and I know what order they're supposed to happen in. I could have produced this array by the order that they were requested in, if that's what I wanted, again, just taking the simple way. Let's assume that I know that file1, file2, file3 need to happen in this order.

So now I can just say, for( var i=0). I less than arr.length and maybe instead of arr maybe I'll call this filenames.
>> Kyle: I++ so I'm going to look at file1. It's going to be filenames of i which the first iteration is going to be file one. I'm gonna say if that is even the responses object.

So filenames of i is in responses or it's not in responses. Can anybody guess what I'm going to do if it's not in responses? Break the for loop. Actually in this case for convenience sake I'm just gonna return just completely empty out. So whichever case I'm in whether I'm looking at file1 and it's not in there.

Or I'm looking at file2 and it's not in there, or I'm looking at file 3 and it's not in there. I've got nothing left to render on this particular pass through my responses. Cuz I know if it's not there I'm not gonna render anything else. So I can take advantage of an early return here.

If it is in there. I can say, if it's still.
>> Kyle: For example, I can say if it's still a truthy value, or I could say if it's still a string. And you'll see why I'm saying that in a moment. If it's still a string then I know it's time to go ahead and render this.

So I'll say filenames I'll say responses of filenames of i. This one is going to need to be the same things as responses of filenames of i. Now, now that I've rendered it. I want to leave the entry in there but I want to mark it as having been rendered.

So a simple way of marking it as having been rendered is to change it from being the text.
>> Kyle: To something like false. So the entry is still in the object, which means I'm not going to try to readd it in there. But I'm not going to try to re-render it over and over again, because it's going to fail this typo check.

There's a million different ways that you could choose to implement this logic. I'm just showing you one possible way of saying. I'm living over this list and I'm checking to see whether I need to render something.
>> Kyle: At the end of this, if I have rendered all three of them,

>> Kyle: Then I can finally output my complete message.
>> Kyle: This function is gonna get called for each of those three times. The happy path is that it gets called once with file1 and then once with file2 and then once with file3 and it never needs to get to this early return thing and every needs to even store thing.

But it might get called once with file3 first, and then once with file2 first, and then finally the third time with file1, where it hasn't rendered anything yet. And that third time, when it goes to loop through, it's going to say I do have a file1 render, I do have a file2 render, I do have a file3.

So which every way these come in, I'm always going to render asap but only in the correct articulated order. Yes.
>> Speaker 2: So I maybe I understood the params that went wrong but I thought you said you wanted to do it by altering the callbacks or did you not say that?

>> Kyle: What do you mean?
>> Speaker 2: So I was thinking maybe we would we have different callbacks for all of the get files and then somehow we would audit them.
>> Kyle: We do have different callbacks for all the get giles.
>> Speaker 2: Okay, that's fine
>> Kyle: I'm not sure I understand what you mean.

>> Speaker 2: I wasn't expecting this intimidation.
>> Kyle: Did you solve it a different way?
>> Speaker 2: No I couldn't.
>> Kyle: Okay.
>> Speaker 2: I was thinking more along the lines of Promises about the start of the callback and something they couldn't come up with anything.
>> Kyle: Well the only way you're going to end up ordering the callbacks is to have sequential requesting.

Where you don't even request file2 until after file1 is finished. Which violates one of the fundamental premises of the setup of the problem which is to call them in order. That's why that's there because it's easy if you just wanna do serial requesting. But it's the parallel requesting that causes this concurrency coordination to be more complex, okay?

The point that's really being made here is that three independent callbacks have to share some closure state even if it's a global variable. They're gonna have to share some closure state to coordinate themselves. That's just, there's no other way around it. Whether it's the global state or whether there's some other wrapper that variables passed to me no matter how you slice it.

There gonna have to be some shared variable cost all three callbacks. And they're gonna have to potentially store something in that variable until it's time to come back and get it. That's what we mean by saying that we're coordinating concurrency, is that we're managing that time state complexity.

And we can see that there's a solution to it but I don't think anybody would argue that this is a good graceful solution to it, right? It's ugly, right? But it gets the job done. And I can't tell you many hundreds of times I've solved similar problems in the past where the only tool I had available to me was functions and closure variables.

So we can get the job done, even complex certain areas. But this isn't the ideal pattern and that's why I'm walking you through it. I'm showing you, first, the pain points that we've been dealing with to motivate why we want better patterns.q Is everybody with me on that?

>> Kyle: Any other questions about exercise one? We're gonna get to come back to the exact same problem domain over and over and over again, okay? You'll get better and better at solving this problem with better patterns.
>> Speaker 3: By the way, I'm not convinced that you're going to return at, like if it comes back file2, file1, file3 I think you might get to complete even after file2, cuz won't it fall through?

>> Kyle: I'm sorry, what?
>> Speaker 3: Won't you get the complete if it comes back, let's say file2, file1, and then file3. But after you get file2, it should print out and fall down through the output complete and then so you get complete then file3 should come back then get another complete.

>> Kyle: You're still loosing me, maybe over a break we can talk-
>> Speaker 3: Yeah.
>> Kyle: I mean you can compare what I've typed here to what I typed in the solution file. The solution file is objectively correct, and maybe I missed something, but I'm pretty sure one of those three passes is gonna hit that thing before it hits the output complete.

You're either gonna be able to go through the entire for loop or you're gonna hit this case, which stops it from getting to complete.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now