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

The "Exercise 4 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 4.


Transcript from the "Exercise 4 Solution" Lesson

>> [MUSIC]

>> Kyle: If we have something that could be an arbitrary length, our go to tool as a JavaScript programmer is often a list, an array. If I were to populate a list of the URLs that I cared about handling into a an array, I then immediately have access to those functional programming techniques like map and reduce to transform, compose, exclude things like this on this list.

Let's try to change this list of URLs to something else. Maybe we should get them into being a list of promises. How do we transform? We call .map. Map is the utility that will transform a list one to one. And we're going to need a predicate function, which takes in some value and gives us a new value back out.

Anybody think of a function like that? How about the get file function? See how it takes a single value in and returns as a single value out that's been transformed? In this case a promise wrapped around the value. Great, and we also know that it's going to go ahead make that AJAX call right away.

Not only does the transformation, but it also takes care of making all three of those requests in parallel for us. We simply pass the getFile as our predicate to map. Excellent. We've done the first step, which is to call all these things. And now we have a list of promises.

None of them have any outputs, sequenced in with them, and they're not sequenced together. Well we talked about a promise chain is just a way to take one promise and return it from the resolved handler of a previous promise. That's a method of composing promises, is to chain them together.

Well composition is handled by our good friend the reduce function. If I call reduce, reduce takes two parameters, two arguments. The first one is the predicate function, so we're gonna have a function here called, let's just call it combine or maybe we'll call it chain that sounds better.

We'll call it chain actually no, I'm gonna call it combine. Here I am being super indecisive and I'll tell you why I decided not to call it chain. And then we are going to need an initial value that's the second argument that gets passed or reduce. What's a good initial value for a promise chain?

How about an already resolved promise. You could manually do that, by making a Promise here, that receives a resolve, and actually calls the resolve right away. That's an immediately resolved Promise. Doesn't wait on anything. Turns out there's a shorthand way for that, some of you might have looked that up or seen that in documentation.

There's a shorthand built into the native Promise API called Promise.resolve. Promise.resolve creates a resolved Promise and if you pass anything into it it resolves the Promise with that value. We don't care about any value here, we just care about the fact that, that's a good initial value to start chaining off of, so that's our initial value as a resolved Promise.

Now, combine needs to take two arguments and what we call those of course, I think it's better to change the names of these parameters to fit the context of what you're doing so that your code makes more sense rather than always using accumulator and current or something. What would the accumulator be called most appropriately here?

I would call it chain. That's why I didn't call the function chain cuz I realized, I'm gonna call my accumulator chain. The very first time this predicate is called, the chain is just gonna be that already resolved initial value Promise. But it's going to continue to be composed more and more of that chain as we go along.

I'm going to call my accumulator chain and I'm going to call my current value. PR for it's the Promise that I'm dealing with. There's my two together, my chain and my PR, now how am I going to compose them? If we think back to what we did in exercise three, the way you compose one Promise to another was to return the second one from the first.

My chain needs to have a .then
>> Kyle: That returns the pr. And that new operation has produced a new chained Promise. I need to return that from my predicate.
>> Kyle: Now that code will chain all for of the Promises together regardless of how many Promises there are. It will create a big old chain of Promises.

Here's what that chain would look like. It would look like p1.then function return p2 .then function, return p3 and so on. We're part of the way there. But what are we missing? We're not sequencing in the outputs into the chain. Where are those outputs going to go? They're going to go right here.

>> Kyle: Now we're going to have a, if I were to write this out. We have a Promise.resolve, is our initial value, .then function return p1. And you notice I have the immediate .then output on it.
>> Kyle: Everybody see how I'm unrolling that, what we're doing in the reduce?

That's the visual unroll of what's happening in our reduce, so it looks pretty good. One thing that's missing is we don't have our complete statement on the end. What is the end result of our reduction?
>> Speaker 2: A single value.
>> Kyle: A single value but what is it?
>> Kyle: It's our promise chain.

What can we do? Just simply put another .then on the end of our Promise, and output to complete message.
>> Kyle: Yes?
>> Speaker 2: The order for our two events, the returning the Promise and doing the output.
>> Kyle: Uh-huh.
>> Speaker 2: Could be in the opposite order like it wasn't our previous exercise?

>> Kyle: No. Because if we did it in the previous order, here's what that would do.
>> Kyle: Do you spot the problem? What are we out putting here? An empty value from our initial Promise. What are we outputting here? p1. What are we out putting here? p2. Who's outputting p3?

The ordering is absolutely necessary.
>> Kyle: All right. That's a utility that we could write. You wouldn't want to write that code over and over again, your maps and your reduces. But it's a utility we could wrap that up into a utility where we pass it an array of values and we tell it to sequence all these responses together and then we could reuse that utility all throughout our program.

>> Kyle: The value of being able to understand our little helper friends like map and reduce and developing an instinct for asking questions like, wait I'm fundamentally doing something repetitively, why not just put that into a list context and let the tools repeat it for me? Sometimes that's a for loop, but sometimes an even higher order pattern like a map reduce will help us out.

>> Kyle: Any questions about exercise four?
>> Speaker 3: One thing they're asking if you go down to all the Promise. What would you name all of those anonymous functions?
>> Kyle: [LAUGH] Yeah well in our silly example here, we don't really have a lot of context to give us good naming for things.

If I were gonna name this anonymous function I would say, chainP1, chainP2, chainP3 because I would be describing exactly what that function does no more or less, and what it does is to chainP2, P1, P2 and P3 into the chain. Up here, if I wanted to name that anonymous function I just call it chain p, chainPr.

My instinct is to always try to find some description for what the thing is doing, even if it's a very generic description, that still better than the word anonymous.
>> Kyle: Any other questions about the exercise? Yes.
>> Speaker 4: Do we get more performance [INAUDIBLE] or any performance doing this map and then reduce of or instead having the one we did in the previous exercises where we and to give the, you the Promises separate [INAUDIBLE].

>> Kyle: And so the question is, is there a performance difference not really. There's no real performance difference this is the primary thing here is functionality. This is a mechanism by which we can have an arbitrarily long list of URLs to weave together. Whereas in the previous example, it's fixed at three.

Because of the way that we wrote it. It's more functionality than performance.
>> Kyle: Yeah, just as a general reminder, if you're having problems with something that I've typed, it is entirely possible that sometimes I flub up and I miss type something or whatever. But the exercise files, the exercise solution files have been regularly tested they should work.

Check yourself against what you've got in the exercise file. That's usually a good place to try to ferret out where there's, there might be an issue.

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