Check out a free preview of the full Advanced Asynchronous JavaScript course:
The "Challenge 4: Solution" Lesson is part of the full, Advanced Asynchronous JavaScript course featured in this preview video. Here's what you'd learn in this lesson:

After reviews the criteria for completing the challenge, Jafar walks through the solution.

Get Unlimited Access Now

Transcript from the "Challenge 4: Solution" Lesson

[00:00:00]
>> Jafar Husain: So I don't think I did as good a job of setting up the problem as I wanted to set it up. So if you remember, the issue is, and we've actually run into at this problem immediately, which is that not all these images necessarily are gonna be valid.

[00:00:11] And so we see this kinda ugly tiny little placeholder broken image placeholder. Instead, I've actually got an image, public domain image that I'd like to display in such a case, somewhere around here.
>> Jafar Husain: I wanna display this particular image if there's an error. And so, I've actually stored that, and it's here in a constant, LOADING_ERROR_URL.

[00:00:39] All right, and so whenever we run across an error preloading an image, we'll just display this image instead. And so, how are we gonna do that? Well remember, preloading works just like most APIs, AsyncAPIs in the browser. It uses callbacks. And so, how are we gonna, basically, the challenge is we wanna create ourselves a preload image function, which returns an observable that either gives us the image, the URL, once it's been successfully loaded, or gives us this constant,

[00:01:13]
>> Jafar Husain: LOADING_ERROR_URL.
>> Jafar Husain: Does that make sense? So let's write preLoadImage, so it's an observable of either the successful image after we've confirmed it's preloaded, or this fallback error loading URL in the event we have an error. So we're gonna create an image object for the preloading.
>> Jafar Husain: And how are we gonna do it?

[00:01:40] How are we gonna build an observable here?
>> Speaker 2: Images switch.
>> Jafar Husain: Well actually, let's not worry about that right now. Let's just worry about this tiny problem. PreLoadImage is gonna return us an observable that either gives us the same source that we pass in if the image was successfully preloaded, i.e., right here.

[00:02:05]
>> Jafar Husain: Or it gives us a fallback constant, excuse me, gives us this image to display right here. So the observable will never error, it'll either just onNext the source that we've asked it to preload, or it'll onNext us this fallback source, so we display an icon onscreen. So how are we gonna build it?

[00:02:26] We could, yeah?
>> Speaker 2: I was just gonna say we could start by returning an observable, but that was-
>> Jafar Husain: Yeah, also you're absolutely right that we could do what we've been doing up until now. We could write a special operator for this. Like we could build something where we return and create a new observable by defining the subscribe method.

[00:02:43] But I'm gonna suggest that we don't need to do that. We can use the observable operators that we've learned to avoid having to write our own definition of subscribe. Does that make sense? So, onload is an event off of image. In addition to doing this, I can also do addEventListener.

[00:03:01]
>> Jafar Husain: Do we know how to turn an event into an observable?
>> Speaker 3: From event.
>> Jafar Husain: Yeah, fromEvent, right?
>> Jafar Husain: Okay, that's interesting.
>> Jafar Husain: That's one down.
>> Speaker 3: So the same with error?
>> Jafar Husain: Error, okay.
>> Jafar Husain: Okay, now what are these observables respectively going to give me in the stream?

[00:03:57]
>> Speaker 3: URLs.
>> Jafar Husain: URLs, but right now they're not gonna give me URLs, right?
>> Speaker 3: Right.
>> Jafar Husain: Right now they're gonna give me an event object that comes in to load. So how do we make it, how do we substitute that event object for a URL?
>> Speaker 4: Map.
>> Jafar Husain: Map, yeah.

[00:04:14] Map's what we use whenever we wanna substitute data inside the stream for other data. So,
>> Jafar Husain: That's map overload, and what are we gonna return? I'm gonna ignore the incoming event object cuz I don't care about it. And what am I gonna return now?
>> Speaker 4: Source.
>> Jafar Husain: .Source, yeah.

[00:04:31] The source that I was asked to preload. And in the case of failure, what do I want the screen to contain?
>> Speaker 4: Same thing but map the source to your constant.
>> Jafar Husain: Yeah.
>> Jafar Husain: So the reality is you should almost never have to write your own subscribe methods.

[00:04:55] As we have seen it, throughout this class, almost every problem, not always, but almost every problem, can be broken down into use of smaller operators. So we're not done yet. What's the last step we gotta do here?
>> Speaker 4: Merge them together and take only the first one?
>> Jafar Husain: Yeah, merge them together and take only the first one.

[00:05:13] So let's return Observable.merge(success, failure). And I could do take(1). Is that what you're suggesting? Is that necessary here though?
>> Jafar Husain: Why isn't that necessary? And it's a totally valid thing to do but is it necessary?
>> Speaker 5: I guess it would either load it or error.
>> Jafar Husain: Yeah, it's only gonna load or error, so take(1)'s probably too much of an abundance of caution here.

[00:05:42] But cool, now we've got a function which'll take, yeah?
>> Speaker 5: Are we relying, with this sort of method, are we relying on the browser than to cache the image? Or is there a possibility that we'll load the image twice since we're doing it on the constructor of image and then as well in setting the source of an image later?

[00:06:02]
>> Jafar Husain: That's a good question actually.
>> Speaker 5: Image will be caching at a load event.
>> Jafar Husain: I believe so. Yeah, we're relying on the browsers even regardless whether you do it with your image which is also a done object.
>> Speaker 5: I was just curious if we get to get like the image content from the event and then actually reuse that, but if it's the browser's just gonna cache it then I guess it's for simplicity.

[00:06:22]
>> Jafar Husain: Yeah, we're just relying on the browser to cache, to avoid us dialing the same image multiply times. So, now that we have this preload image function, which is a handy function. How are we gonna modify our code so far to avoid, let me put that closer to a code.

[00:06:38] How are we gonna modify our code to avoid displaying broken images?
>> Jafar Husain: Did I do that right?
>> Jafar Husain: So let's remember where we are here, right? Every time a new sub comes in, we attempt to load all of that sub's images, and we return a stream of all the selected indices in the images for that sub.

[00:07:11] Every single time a new sub comes in, we switch to the images for the latest sub. And then once again, start returning the images for each selected index. Now, we need to somehow transform this image, or this image URL, at least we need to do a preload. How are we gonna do that here?

[00:07:35]
>> Speaker 5: We map to preload.
>> Jafar Husain: Yeah, so here, right? This particular, this right here is a stream of all the selected images. If we map over that,
>> Jafar Husain: We will get a URL, and we could call preloadImage on the url. How many dimensions is this sub observable right here?

[00:08:02] Now, how many dimensions is it?
>> Jafar Husain: Well, we mapped over every item in an observable and we return, what is preloadImage return?
>> Speaker 5: An observable.
>> Jafar Husain: Another observable, and so now we've deepened the dimensionality of the observable. And so, every single time you select a new image, we're gonna begin preloading it, and make a network request [SOUND].

[00:08:28] How do we flatten it down? What strategy do we use?
>> Speaker 5: Switch or doesn't really matter, merge.
>> Jafar Husain: Well I think it matters because if somebody hits next, next, next, next, next, we're gonna start preloading four images concurrently, right?
>> Speaker 5: So switch.
>> Jafar Husain: Yeah, if we did merge, then the last image to finish loading would be the last one to come out, but it's not necessarily the last image that they selected when they press next.

[00:08:54]
>> Speaker 5: Right.
>> Jafar Husain: So which one, which flattening operator am I gonna use?
>> Speaker 5: Switch.
>> Jafar Husain: Switch, yeah.
>> Jafar Husain: Gotta put that in the right place.
>> Speaker 6: Your params is off.
>> Jafar Husain: Got my params wrong.
>> Speaker 6: The one up above it. That one.
>> Jafar Husain: This one.
>> Jafar Husain: Did I do that right?

[00:09:26] Probably.
>> Speaker 6: Probably.
>> Jafar Husain: And dot.
>> Speaker 6: Yep.
>> Jafar Husain: Yeah, now there's a couple of ways you can nest this. I prefer to do it in this particular style,
>> Jafar Husain: Which will also work.
>> Jafar Husain: So we can flatten twice and then that, I just prefer to keep doing this.

[00:09:58]
>> Jafar Husain: Did I do that right? Sorry, let me back out here.
>> Jafar Husain: Before I preorder this, I just wanna make sure it's working correctly. I think that's right, right?
>> Jafar Husain: Let's give it a shot.
>> Jafar Husain: It's like I got an error
>> Jafar Husain: Possibly.
>> Speaker 7: In the download is the image on it?

[00:10:41] Element source set to, just for debugging purposes.
>> Jafar Husain: What is the image element source set to?
>> Speaker 7: Yeah, just like in the DOM, and that-
>> Jafar Husain: Check it out. Yeah.
>> Speaker 7: Yeah.
>> Jafar Husain: Good idea.
>> Speaker 7: Just to debug it.
>> Jafar Husain: I think it's just not getting set. So somewhere we lost.

[00:10:59] We lost.
>> Jafar Husain: So for each one of the images coming out, we're mapping that under preloadImages and we are calling switch on that. So I'm gonna actually see if we are even getting into preloadImage.
>> Jafar Husain: Yup, so one possibility is that we're not firing in here somehow. On next or something is not firing.

[00:11:36]
>> Jafar Husain: Just make sure that this is indeed an image.
>> Jafar Husain: It's not, but that would explain why it's hopefully going to fail.
>> Jafar Husain: And so, now if I wanna further debug this, right? What do I have to do? I can't really step into anything. By the way, there's an upcoming version of Chrome.

[00:11:56] I don't have it right now. Cool, look.
>> Speaker 5: That's awesome.
>> Jafar Husain: Everybody see that?
>> Speaker 5: [LAUGH]
>> Jafar Husain: That's actually pretty recent so, now let's see what the hell is going on.
>> Jafar Husain: Are we actually making it in here which is where we expect it to be, right? Functional programming just got a lot more debuggable as you guys can see.

[00:12:18] Now, so it looks like what we expected to happen didn't happen. So let's try that one more time and see where we went wrong. So we expect that error is fired, but we're not seeing it fire. Now to test that, I'm probably just gonna confirm my assumption that image.

[00:12:40] I'm not crazy, right? That was your assumption as well, right? That's what I thought.
>> Jafar Husain: Okay, can anybody think of another reason why that wouldn't fire, out of curiosity? Maybe we don't understand how image works, but maybe we do. Assuming image does work this way, what's another reason why that callback would never fire?

[00:13:17]
>> Jafar Husain: Well, maybe we never subscribe to the observable, right? In this case, we absolutely should. The switch operator is receiving these observables it should be subscribing to each and everyone, but it's interesting to think about, that's the thing about laziness, right? If nobody subscribed to the observable, that's one possible explanation for why that's not getting in there.

[00:13:35] So,
>> Speaker 4: Failure is undefined.
>> Jafar Husain: Where does it say that?
>> Speaker 4: It's when you click on it, it said failure undefined.
>> Jafar Husain: I wouldn't be worried about that. I think it just hasn't gone through defining that yet.
>> Jafar Husain: So maybe I'm pretty sure that's how preloading works so, I must be making some other obvious error.

[00:14:06] All right, so I'm not crazy I'm pretty sure that's the way preloading supposed to work so we're probably just doing something else. Obviously wrong. No, I'm just gonna Google it. [LAUGH]
>> Jafar Husain: Is it maybe error?
>> Jafar Husain: Or maybe it doesn't have an addEventListener, remove that listener. Anybody out there in cyberspace know how to preload an image?

[00:14:48]
>> Jafar Husain: I have tried this already once so I'm pretty sure it's the right answer but I'm probably some sort of typo or something.
>> Jafar Husain: No.
>> Jafar Husain: So there's an on load.
>> Jafar Husain: I see a non-error.
>> Jafar Husain: Lots of happy cases in here, but I don't see the unhappy case.

[00:15:37]
>> Jafar Husain: There's at least one other person who thinks this is the right way to do it.
>> Speaker 6: I found a stack overflow where they're having the same problem.
>> Jafar Husain: [LAUGH]
>> Speaker 6: And the solution was use jQuery bind to error.
>> Speaker 5: [LAUGH]
>> Jafar Husain: Usually the solution to DOM problems, right, use jQuery.

[00:15:54]
>> Speaker 6: From the chat, [COUGH] someone's asking is it, that the element isn't inserted into the DOM?
>> Jafar Husain: No, you shouldn't have to insert the element into the DOM, as this guy's showing right here. But it's a reasonable guess. I'm sorry that what's stack.overflow say, why do I need to use jQuery, is this a browser bug?

[00:16:12]
>> Speaker 7: Just that they added a event listener to image and it wasn't firing. But with jQuery bind, that was the solution, using jQuery to bind to the error event. Maybe it doesn't throw error events, I don't know. But it has like callback for on error.
>> Jafar Husain: Okay, I don't like that answer, but-

[00:16:34]
>> Speaker 7: [LAUGH]
>> Speaker 5: So they're saying online too, they want me to ask you if the image object we create and preload image would trigger events if it's not inserted in the document?
>> Jafar Husain: Yeah, yeah, we got that question. No, that's not the issue, definitely not the issue. Okay, well I got this working before.

[00:16:52] Maybe we just got unlucky with the right set of circumstances. What I'll do here is, doesn't say why jQuery [LAUGH] bind actually works. Nobody? They don't go in any more detail, cuz under the hood jQuery bind's doing something.
>> Speaker 5: Well, it might be using the onerror callback function?

[00:17:13]
>> Jafar Husain: We're using the onerror callback function, but the last ID I have here,
>> Jafar Husain: Is this.
>> Jafar Husain: And if that doesn't work, we'll have to move on and try something else but,
>> Jafar Husain: Pretty sure that's not gonna fire.
>> Jafar Husain: Okay, we have something that's not an error, or not an image rather.

[00:17:46]
>> Jafar Husain: Nope, [LAUGH] all right, well that's why web programming is hard.
>> Jafar Husain: No idea why that's not firing.
>> Jafar Husain: We'll just have to live with that issue for now. I'm gonna comment a preload image, and we're just gonna live with the browser provided, broken image I have. And I'm sure we can get to the bottom of that.

[00:18:09] I just don't have time in this class. But long story short, had preload image worked? That's exactly what we would do. We would once again switch because we can preload images, but as soon as somebody switches to a new one, we wanna stop preloading, etc.