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

The "Indices and Switching Subs" Lesson is part of the full, Advanced Asynchronous JavaScript course featured in this preview video. Here's what you'd learn in this lesson:

With the image viewer app prototype working, Jafar continues to debug and refine the capabilities. Jafar takes questions from students.

Preview
Close

Transcript from the "Indices and Switching Subs" Lesson

[00:00:00]
>> Speaker 1: So I think we're actually in good shape now for it, if we can live with the browser's ugly missing image icon. I think we're actually in pretty decent shape.
>> Speaker 1: And so hopefully if I press Next, we might get a real image this time. Ooh, what happened?

[00:00:19]
We're not quite where we wanna be.
>> Speaker 1: [LAUGH] I think it's that I'm setting the loading style to visible, when they are, I'm setting, actually, visibility to hidden here. I'm not sure why that's not going away, let's see.
>> Speaker 1: I know why. It's the order in which I subscribe, possibly.

[00:00:50]
>> Speaker 1: Does anybody see what's going on here? I'm subscribing to actions after images, and it's possible I'm actually getting the images [LAUGH] after the actions, but that's another good thing to talk about here. So I'm gonna reverse this just to make sure that that's the issue, but then we'll talk about a more robust way of making that work correctly.

[00:01:12]
>> Speaker 2: Quick question.
>> Speaker 1: Yeah.
>> Speaker 2: In your preload images function, you have the image, img, right?
>> Speaker 1: Yeah.
>> Speaker 2: But also at the top of your script, don't you have another one?
>> Speaker 1: Where? I see what you're saying here. I don't think that's the problem, because this should mask the global scope, but that's a totally valid observation.

[00:01:43]
I'm pretty sure that's not-
>> Speaker 2: Lexical scope, are they not?
>> Speaker 1: Yeah, yeah, so I'm pretty sure that's not the problem. I'd be suspicious, too, but the fact that it's defined more at lexical scope, then I'm not gonna be able to hit the one in the global object, but we can try it.

[00:01:59]
Who knows, weirder things have happened. But let's come back to that one, and just make sure we get the, why don't we try and just make sure we get this example working and then we can come back and try the preload image.
>> Speaker 2: Sure.
>> Speaker 1: So I think, let me remove these annoying debuggers.

[00:02:29]
>> Speaker 1: All right. So, we have an image viewer. Well, actually I don't know if back will work. Okay. It seems to be working, it seems to be working. What if I switch to this?
>> Speaker 1: Well, okay, got a few broken images in there, but.
>> Speaker 2: What if you refresh and press back?

[00:02:52]
>> Speaker 1: Refresh and press back.
>> Speaker 2: Yep.
>> Speaker 1: As in?
>> Speaker 2: Index negative 1, yep.
>> Speaker 1: You mean like?
>> Speaker 2: Refresh the page.
>> Speaker 1: Reload.
>> Speaker 2: And now press the back button.
>> Speaker 1: Yeah, that's a good point, what's gonna happen?
>> Speaker 2: Exception.
>> Speaker 1: Well, technically, I think it's actually a, it's working, it's just a broken, cuz we're not doing the preloading status, it's just a broken image, but it's gonna go off the end of the array.

[00:03:17]
So we don't want that, but I think there's also a more serious problem than that, which is, if I go to cute, and I go next, next, next, next, next, and I switch to pics, what index am I gonna be at?
>> Speaker 2: Whatever index you're in that time.

[00:03:38]
>> Speaker 1: Sure, I'm not actually sure that's true, but let's find out. So it's a thing to be worried about, right? We probably wanna restart index at zero. Let's find out if we are actually gonna restart at index zero. What's our intuition here? Where is it? I lost the code.

[00:03:56]
It's so small I lost it. There we go. That's pretty good. Are we actually gonna restart? Are we gonna be at the same index we were, knowing that indices is defined up here?
>> Speaker 1: Sorry, bear with me.
>> Speaker 1: All right, preloadImage, syntax highlighting is broken there but.
>> Speaker 1: Does everybody see that?

[00:04:32]
What do we think? Am I gonna be at index zero, one, two, what am I gonna be at?
>> Speaker 2: When you switch the sub?
>> Speaker 1: Yeah, when I switch the sub.
>> Speaker 2: I'm assuming you'd be at whatever indice you left at?
>> Speaker 1: Still be at whatever indice I'd be left at, interesting.

[00:04:50]
Everybody agree with that?
>> Speaker 1: Let's say I subscribe once to indices. I'm gonna start out with a zero, right? And then if somebody hits left, right, left, right, left, right, left, right, or right 15 times, and then I resubscribe the indices, what's the first thing that's gonna hop out of there?

[00:05:15]
>> Speaker 2: Still be zero?
>> Speaker 1: It'll be zero.
>> Speaker 2: Did each of the subs subscribe idices so that your subs are gonna maintain the indice that you are on?
>> Speaker 1: Because we return these indices observable, right? And then each one, everytime a new sub comes along, and then somebody comes along and subscribes the indices again Because indices is gonna be lazy, every single subscription is gonna start its own counter at zero.

[00:05:40]
Does that make sense?
>> Speaker 2: When you click from cute to pics back to cute you're on the same indice you left cute on, right?
>> Speaker 1: No actually.
>> Speaker 2: No.
>> Speaker 1: I'd start back at zero.
>> Speaker 2: Start back at zero again?
>> Speaker 1: Yeah. If we wanted that behavior we could do something about it, but this goes back to why we started by implementing observable bottom up.

[00:05:59]
It's if we were to go and implement scan right now, the first accumulator, if somebody subscribed we'd just on next them that value, but then the next person who subscribes we're going to on next them zero all over again. Every subscription gets its own counter, and so that's why we're actually gonna start from zero every time.

[00:06:15]
Cool? Is there, now we did go off the end of the array. Let's see if we can do better. How do I avoid going off the end of the array?
>> Speaker 1: Should I index suggest?
>> Speaker 2: You do a check. You're gonna have to do-
>> Speaker 1: I could check?

[00:06:37]
>> Speaker 2: Yeah.
>> Speaker 1: Yeah?
>> Speaker 2: To see if it-
>> Speaker 1: Well this is a PM question, how do we want the application to behave?
>> Speaker 2: You've gotta do it on the indices, right, because if you do it in the subs sure you're never gonna load the wrong image, but your indices are still gonna moving in the wrong direction.

[00:06:59]
>> Speaker 1: Well, on the right index the right bounds for the index depends on the size of this images array, which is different for different subs, right? So we can do the simple thing, which is to just filter out invalid indices, so if somebody hits back on zero, and we see negative one, we can just filter it out.

[00:07:16]
In other words, back would basically stop working when you got to the edge. I'm fine for that for a first pass. We're not gonna ship this at the end of the class, but we can also try I think the behavior that a lot of us would find more intuitive, which is the old wraparound.

[00:07:31]
So do we wanna start by just filtering out invalid indices? How are we gonna do it?
>> Speaker 2: Just do a filter for indices less than zero.
>> Speaker 1: What else is invalid?
>> Speaker 2: Greater than the length.
>> Speaker 2: Equal to the length, equal or greater than.
>> Speaker 1: Yeah, I think it's actually we wanna go the other way, right?

[00:08:10]
>> Speaker 1: Cool, now we shouldn't go off the edge anymore, and you guys can probably figure out how we'd do the wraparound. It involves math. Nobody likes that, but it's pretty straightforward.
>> Speaker 1: So it's too bad we couldn't get the preload image working, but I think you guys saw how that is structured.

[00:08:30]
We just keep going another map, we create another level, and then we switch back, and so if every single time we increase the dimensionality of the observable, we apply switch to it, we know we've never gotten the erased edition. So if somebody hits next, next, next, we're not gonna be in preloading five images, and then the fastest image to preload wins, not the last image bay selected.

[00:08:50]
So does that make sense, guys? So that was the last exercise I had for today. We built ourselves a litter Reddit image viewer. I was gonna open it up for some questions, because we've got about half an hour left. Was there anything actually we wanted to learn about that we didn't get a chance, yeah?

[00:09:06]
>> Speaker 2: Can we just try the preloader again, but just change the-
>> Speaker 1: Yeah.
>> Speaker 2: Name of the img variable?
>> Speaker 1: Yeah, let's try it. Let's rule that one out.
>> Speaker 2: I think it was trying to use that, the dom img.
>> Speaker 1: Yeah, absolutely. At the very least, we can put this back, put it back in.

[00:09:23]
>> Speaker 2: In the chat it seems like they figured something out.
>> Speaker 2: They didn't post the code, but.
>> Speaker 2: If you could post the code that would help.
>> Speaker 1: Source after on error. I don't think you set the source, so it's a totally reasonable question, which is should you set the source after the on air, and isn't that a problem after attaching the variable?

[00:09:47]
I don't believe that's a problem, because in JavaScript we don't give up control of the thread until our block of code has finished executing. So newing up the image and setting it in the constructor, it's not gonna suddenly erase and then try and fire on air. The one question I have is, does it actually go in the constructor, and you know what?

[00:10:09]
That might be what's going on here. I think I passed it in the constructor instead of assigning it to the source properties. So let's try that now.
>> Speaker 1: See that? I put it under constructor.
>> Speaker 1: Now it's interesting to note that, have I created a hot observable here?

[00:10:42]
Notice that I'm doing work before someone subscribes to this observable. Remember when we re-ordered that initial function and map, I think it was map, or it was set time out where we kicked off the set time out and then we returned the observable? The danger there is that somebody's going to subscribe that observable too late and they're going to miss the time out.

[00:11:03]
And that is technically, in practice it's not really gonna happen here, but there is, it's worth noting that this is generally something you want to avoid doing, cuz I've created erace here. In practice, we're gonna subscribe to this observable immediately, so we're not gonna miss the message, but in general, this is a bad idea, and if I subscribe to this same observable five times, I'm not gonna try and preload the image five times also.

[00:11:25]
You notice that, right? That's not gonna behave that way. Maybe I want that, maybe I don't want that. I probably don't want that in this case, but in general, you should be very careful if you're kicking off work and then returning an observable, because if that work is gonna cause the observable to fire, you've created a race, you've created an observable that's hot.

[00:11:44]
How do we make sure that all of this work, that the work that actually happens to kick off the work behind the observable, fires every single time somebody calls subscribe on it?
>> Speaker 1: Well, we could write our own definition of subscribe, like make our own observable like we did before, but there's actually a function that allows us to encapsulate this pattern of waiting until subscribe is called to do the work associated with that observable, and it's very simple, it's called Observable.defer, because we defer work.

[00:12:17]
So instead of doing this work as soon as the function starts,
>> Speaker 1: You can call defer and pass in a function, and the contents of this function is gonna run every single time subscribe is called. So this is almost like defining our own subscribe, it's just a little bit simpler.

[00:12:39]
The difference is, when this function runs, we're gonna do all this work, set all this stuff up every single time subscribe is called. It's only that this is gonna run when subscribe is called. The last thing you do is you actually return the observable and then defer subscribe to that observable.

[00:12:57]
So it's sort of like taking an observable and instead making an observable factory. It still looks like an observable from the outside, but inside, whenever you subscribe to it, it's creating a new observable internally, and then forwarding your subscription to that. Does that make sense? So it's kind of like an easier solution than writing our own subscribe function.

[00:13:16]
It's simpler, we just sort of do a bunch of initialization work, and then we return an observable, and that happens on every subscription, not on every call to preLoadImage. So in this case, this would be the fastidious way of solving this problem that makes sure we don't have any erases.

[00:13:33]
We're not gonna even begin to preload until somebody calls subscribe. Is that clear how that works? Right? Is it clear why if we would've begun the work of preloading before subscribe was called on observable before, and now that's not the case anymore, because of the way defer works?

[00:13:48]
It defers the work of setting up a subscription until subscribe is called on the observable. So now we've made an observable without any raciness associated with it, and I think this time I'm feeling good about it, I think we're gonna get it working. So let's put back in our preload code here.

[00:14:07]
>> Speaker 1: So my goal is to hopefully get the entire solution of this problem, at least the main solution of this problem, on one very high font sized screen. So let's see if we can make that work. So here I'm gonna map over. Remember over here, this smaller sub observable here are all of the images coming out.

[00:14:25]
This is this a flat stream of URLs. So we're gonna map over it, and we're gonna call preLoadImage(url), and we can just be clever and just shorten that to preLoadImage. And we're also gonna switch, right, because [LAUGH] switch, for the reason we said, which is if somebody hits next for the next five times, and we begin preloading five images, we want the last image that they selected, not the last image that happens to preload, finish preloading, or the first image, excuse me, that happens to, or last image.

[00:14:57]
So I'm seeing some weird syntax coloring, but I think that's actually just a bug in Adam, because-
>> Speaker 2: It might be that last print on 76.
>> Speaker 1: Maybe you're right, yes.
>> Speaker 2: I'm running into the same thing on Adam.
>> Speaker 1: You are? Interesting. But you were right, I think that last print on 76 was wrong.

[00:15:20]
>> Speaker 1: And all of this, we create this observable of images that have been preloaded, and are selected when somebody moves their mouse for every single time we get a new sub. We begin this process again. So fingers crossed, guys.
>> Speaker 1: Do I have a debugger statement open here?

[00:15:46]
>> Speaker 2: I think there's an error in your console log.
>> Speaker 1: There is, thank you.
>> Speaker 1: Uh-oh.
>> Speaker 1: I probably have an unopened comment.
>> Speaker 2: At the end of preload image.
>> Speaker 1: Yeah, thank you.
>> Speaker 1: So we know that the first time that, let's put the, let's try out this fancy new hotness from Chrome, shall we?

[00:16:20]
See if we get in there. No. So close. Let's see if we actually even got into preLoadImage.
>> Speaker 1: Nope. I'm not sure what I did to change that, but it feels like we should be in there.
>> Speaker 2: Keeping with the same defer pattern in preload image, could we try observable from promise, and then use fetch, and then catch the error, and map that error, or set off the error for the loading error url?

[00:16:59]
>> Speaker 1: Yeah, we totally, yeah.
>> Speaker 2: You just need to move the parentheses.
>> Speaker 1: Did I just put the parenthesis in the wrong place?
>> Speaker 2: Yeah, so go to, yep.
>> Speaker 1: Really, it looks right to me, atually.
>> Speaker 2: Yeah.
>> Speaker 1: Where did you think?
>> Speaker 2: Cuz I had it on the inside.

[00:17:21]
The filter isn't working for me.
>> Speaker 1: Filter, where index is larger or equal, so that might explain why we're not getting image, if the filter isn't working.
>> Speaker 1: Well, let's-
>> Speaker 2: I commented that it works.
>> Speaker 1: Okay.
>> Speaker 1: It sure seems like that's the right filter case to me, but let's come back to it.

[00:17:49]
This is all good, cuz this is what you have to do when you're writing a program with observable. I was kind of hoping this would happen so that we could step through the debugging tools, and I'm also particularly excited about that Chrome break point inside of functions, that's gonna make my life so much easier.

[00:18:06]
So let's just step through and see if we get that image. It looks like your story checks out here. And we can get this big image. So let's see what's screwed up about our filter. Do I have a wrong case in my filter here? Meaning like,
>> Speaker 1: [LAUGH] I'm trying to filter an image.

[00:18:28]
You can't filter an image URL, right guys? Let's move that up, cuz that's less dumb, and now we're good. Right, does everybody see what I did wrong? I mapped in the images before I tried to filter it. All right.
>> Speaker 1: I'm so confident I'm just gonna close this debugger window.

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