Spiking the Typing Effect0:00
For this next refactoring workshop, I'd like to discuss the difference between spiking out code and actually thinking about it. So to illustrate this, we'll use a simple example of the typing effect that you see here on the Laracast home page. We'll write it first by spiking it out very quickly, and then we'll take a few moments to actually think about it. Okay, we have a blank file, we're ready to go. So let's pull in Tailwind for some very brief styling. We'll add a section here with a heading of Laracast. All right, so if we take a look, this, of course, is what we get.
And yeah, I mean, I think that looks decent enough to get us going. So let's add all of that, grab the headings, switch on back, give it a look, and I think we're ready to go. All right, so for the typing effect, think about it. We want to sequentially remove the last letter. So get rid of the S, then wait a bit, then remove the T, then the S, then the A. So let's brainstorm some ideas we might consider. So first, a little tip. If you select any element on the page, you can then access that element or that node using $0.
Now if we did it again, we'd remove another character and another. OK, so yeah, we might start by doing something like set the innerHTML equal to what it currently is but slice off the last character. And now it's lyricast. Let's do it again, lyricast, lyrica, lyric, lyra. You get the idea. So now if we called this logic every 100 milliseconds, it would appear as if you were backspacing or clearing the text. So let's do this. Let's reset it real quick and figure out how to do the sequence.
So let's do this. Let's reset it real quick and figure out how to do the sequence. Well, setInterval helps with that, right? So we might say, well, to start pretty slowly, maybe 300 milliseconds. We'll paste in that code from earlier, and now this will run three times a second. And yeah, it works. But one issue. Well, a couple issues. First, it's a little too slow. And second, this interval is never going to expire.
First, it's a little too slow. And second, this interval is never going to expire. So for example, if I copy that, give it a refresh, once again find my heading, we'll save it. And then if I run this, but I will also say console.log running, just to give you an idea, this interval will never end. And you'll notice over here, it keeps incrementing long after the text has been deleted. Nonetheless, though, we have some basic ideas. So let's add our script. And I'm going to do this globally, because remember, we're in the spiking phase currently.
So let's add our script. And I'm going to do this globally, because remember, we're in the spiking phase currently. So we'll say the heading equals document.querySelector(h1). And then I'll paste in that interval. But it sounds like we first need to check, are we done clearing the text? So you might say, well, if the innerHTML's length is zero, then you're done, right? So we could clear the interval. We got to give it a name, and then we can clear it. And in fact, I'll just say alertDone for the visual. All right, let's have a look.
Typing New Text Sequentially4:46
What should we do after that point? Well, remember, you can't do that down here yet, because the interval would still be running. So we're in a spiking phase. We're just going to wait until the text has been cleared, and then we move on. Type the new text sequentially. Okay. Let's hard-code it. We'll say the new text is, how about Laravel? And yeah, if we were just to do it all at once, just to give you an idea here, and in fact, let's make it a little faster.
And yeah, if we were just to do it all at once, just to give you an idea here, and in fact, let's make it a little faster. We run it, we remove all the text, and then you write the new text. But of course, like I mentioned, we want that to be sequential, one character at a time. Now what you might do is, well, if you want to iterate over it, you could always say newText, and you sort of want to say, like, forEach character, but you can't call forEach on a string. But we could split it into an array. So this would give us something like L, A, R, you get the idea. So split it, and then for each character, we could type it.
You're iterating over an array, and then you call setTimeout, and then you run something. But what that means is you'll get here before you get to the timeout callback. And so that loop is going to keep running, even though the timeout hasn't fired, which effectively means you've gone through every single character before the first timeout has even completed. So often you're trying to think, well, I want to wait before I continue on. I'll show you some ways to deal with that using promises. But to start, what if we just created a function like type? This function will set a timeout where it updates the heading with the given character. Or in fact, why don't we change this from character to text?
This function will set a timeout where it updates the heading with the given character. Or in fact, why don't we change this from character to text? OK, so now we've defined a function. We're still spiking it out. This function waits a second, and then it updates the iteratedEmail equal to what you gave us. So if I were to say foo, and we give this a run, there it is. OK. But what if this function called itself recursively? For example, if I called type Laravel, that would receive that text, we would wait a second,
But what if this function called itself recursively? For example, if I called Laravel, that would receive that text, we would wait a second, and then why don't we type the first character of that text, and then call the function recursively. But then let's chop off the first character. So if we had Laravel, we want it to then be Aravel. So a little tip, you can use substring, the full version, and that will take off the first character, but include all of the rest. OK, so are we clear here? We are typing Laravel. It will do that by waiting a second, and it adds the first character, and then it calls
Yeah, you saw those undefined a second ago. But it's working, but it never stops. So it always says undefined. It sounds like we should say, if h.innerHTML.length is equal to the newText.length, and I'm sorry, I hardcoded that. If that's equal, then we're done. Or I could even say, if it's not equal, only in that case should we continue calling this function. So this would be one way to handle this. alert, done typing.
So this would be one way to handle this. alert, done typing. So let's give it a shot. We run it, it moves all the text, it types the new text, and then we get the alert. First iteration, done. But again, pretty spiky, and we still haven't allowed for different choices. So think about it. If we had let choices, these would be the different characters we would type. So for example, Laracast, or Laravel, or Programming, or php, whatever you want for your site. We now need to iterate over those.
So for example, Laracasts, or Laravel, or Programming, or php, whatever you want for your site. We now need to iterate over those. So it sounds like I need to record which one we are currently on. Then right here, new text can be choices, and then whatever the current index is. So new text would be this. But then the next time it runs, maybe we update index. So it would point to this. All right, let's see. Notice I'm already having trouble. This is, what, 20, 25 lines, and I'm already having trouble keeping track of at what point
We're done. I can get rid of that. Wait a few seconds. Well, setTimeout. And yeah, you'll notice with things like this, if you're always reaching for setTimeout and setInterval, it gets messy really fast. And I'm going to show you some ways to clean that up. Wait a few seconds. Then increment the choice, OK? So that would be index++.
Then increment the choice, OK? So that would be index++. Because remember, if we increment the index, well, the next time we repeat this code and it does choices[index], that will point to the new value. And then repeat what is effectively all of this, right? So now, let's make it a function. Clear and type. Bring this down here and reformat. And yeah, let's see. Clear and type.
And yeah, let's see. Clear and type. And I have no idea if this would work, but let's try to go through it together. Function clear and type will now be triggered when the page loads. That begins by, well, now we have a comment that isn't quite right anymore, and this is something to be careful of. So I will remove that. That function sets an interval for every 200 milliseconds where it checks if we are cleared. So check if the text has been fully cleared and it does something.
cleared. So check if the text has been fully cleared and it does something. Otherwise, backspace effectively. Once you're done backspacing, we will clear the interval, and then we will figure out what the new text is, and then we will type it out. We do that by calling this new function type where it sets a timeout for 200 milliseconds. It will type a single character, as you see there, and then it waits three seconds, it increments the index, and then it starts the whole thing over again where we set a new interval. Okay, so I have no idea if this will work.
interval. Okay, so I have no idea if this will work. Let's give it a shot. We remove it, we type out Laravel, and then three seconds, okay, and what was next? Programming, and then we wait three seconds, one, two, three, and I think it's still going to break. PHP, have a look at the console. Yeah, so notice we never check to see are we at the end of our choices. We increment index, and at some point, it tried to do, where is it? Right here.
We increment index, and at some point, it tried to do, where is it? Right here. At some point, it tried to do something like choices[3], and at that point, it's undefined. So maybe when we go to the next choice, it's not enough to simply say index. We should say index equals, or actually, let's do this, index plus plus, and we could say, well, if the index is now greater than the number of choices that we have, and actually, this is zero-based, isn't it? So I would need to subtract one, because remember, choices.length will be three, so we have to subtract one to account for the zero base. So if the index is greater than choices.length minus one, then reset the index to zero.
Initial Refactoring Pass15:38
Where might we start? Hmm, I'm going to start with the obvious. I'm not a big fan of single-letter variable names, unless it's something like i for index. When you come back six months from now, it's not immediately clear what h refers to. So my first thought is to rename this to something like heading. And now that will update all of the references. Okay, next, we're looking for small refactors here. And as a little tip, this is where I think comments can be useful in the initial phases. The comments are sort of a flashlight that points to things that weren't clear when you wrote them out.
The comments are sort of a flashlight that points to things that weren't clear when you wrote them out. And that's really what refactoring is. It's restructuring things to be more clear and reusable. So for example, if we scroll up, yeah, things like this. Check if the text has fully been cleared. Well, why don't we extract that to a function? How about isHeadingCleared? And all we're going to do there is grab that logic and return it. Okay, so now, if isHeadingCleared, then continue on.
And all we're going to do there is grab that logic and return it. Okay, so now, if isHeadingCleared, then continue on. But now notice the comment and the line below it are basically identical. Check if the text has fully been cleared, if isHeadingCleared. Close enough, which means I can remove the comment. Next, this section here represents how we would increment the choice. So how about... Well, really, it's not incrementing, as we've learned here. Sometimes it resets. So we're really getting the next choice.
Sometimes it resets. So we're really getting the next choice. Then set the next choice. All right, how about a function called nextChoice? And we can do this. Come on back. nextChoice. Place that in. And we don't have to do this, but just in case we need it, we will return the index. Now if I scroll on down, set the next choice.
But yeah, be careful. This often happens in real projects where the comment has no bearing on the real code you have here. So I'm going to remove that completely. Next repeat, I think, is somewhat redundant. And this is what we have here. OK, still a lot of work to do. But we can keep going. Next, I have a comment called backspace here. So this is what I often mean when I say look for the low-hanging fruit.
Next, I have a comment called backspace here. So this is what I often mean when I say look for the low-hanging fruit. The low-hanging fruit is obvious indentation that you can remove. Low-hanging fruit is a comment on top of code that isn't clear. Well now, make the code clear and then remove the comment. These are small refactors. backspace and we'll say, how about up here? Function backspace. All right. So now the comment is redundant.
So what next? Let's have a look. Well, my eyes go here. So notice this Mighty Ducks level flying V. All that indentation, all the way to here, and then it goes back down. You want to avoid the flying V, as they learned when they got older in Mighty Ducks 3. This isn't the Pee-wees. Your little duck tricks are not going to work at this level. So let's see if we can remove some of it. First I have a function within a function, which is fine.
So let's see if we can remove some of it. First I have a function within a function, which is fine. But if I get rid of it, I get some free de-indentation, if that's a word, and I think that would still work. I don't know. Let's run it. And we only have to see... Oh, nope. That broke the code. How come?
That broke the code. How come? Let's see. New text is not defined on line 42. Ah, yeah, of course. So why don't we say, hmm, add one more global. Don't worry. We're going to get this clean. Let's initialize it up there. Then add it here.
Let's initialize it up there. Then add it here. And I think that'll do it. Write it on out. And yeah, we're good to go. And then it writes the new text. OK. So a little better. Next, notice we have this temporaryVariable that we're referencing up there. But maybe we could do this.
Next, notice we have this temporary variable that we're referencing up there. But maybe we could do this. We're going to call choices index. So that's the newText. We accept it there. But now notice newText is never being set. And we still need that. So maybe we can have originalText. Is this weird? Let's see.
I have no idea. Did that work? Yeah. Oh. No, it didn't. Huh. So let's see. Yeah. That's because at this point, we call the function recursively, but we never provide the original text.
That's because at this point, we call the function recursively, but we never provide the original text. And actually, I'm not sure if this is better. I think it might just be more confusing. But it looks like it works. And I'm going to stick with it. And if we decide to remove it later, we will. OK. What else? At least we are extracting functions.
And then type the new text. Otherwise, hit backspace. So I'm going to grab all of this. And let's see. Let's put it at the top. Because it sounds like this is really like our, it's like our main function. It's the init function. So maybe, why don't we call that init? And then down here, we'll have that. So here's all of our setup.
Introducing a Heading Class22:24
And then down here, we'll have that. So here's all of our setup. And then we get the project rolling. Yeah. So now the next thing I'm seeing here is this heading variable is referenced everywhere. All of the functions know about it. So it's almost like we are operating on the heading. So if we thought about this in more of an object-oriented way, what if heading was its own class, like Heading? Then when I would instantiate that heading, I would need to let it know where the DOM
own class, like class Heading? Then when I would instantiate that Heading, I would need to let it know where the DOM node is. So I could do that here. And then we would accept that element and assign it. OK. So now we have a class that represents one of these fancy headings. Right now I'm calling it Heading. Maybe you want TypedHeading or whatever. I'm just keeping it simple for now.
Maybe you want TypedHeading or whatever. I'm just keeping it simple for now. If we did that, we can take some of this behavior we've been thinking about, like backspace, checking if it's cleared, typing things. Those are all things that can act on the heading, right? So think about it. If I had a Heading class, then we could have a backspace method directly here. But one step at a time. If I want to migrate over to this heading, I no longer have this. This would then become an instance.
If I want to migrate over to this Heading, I no longer have this. This would then become an instance. And then, just to make sure everything isn't failing, for the time being, I would have to grab all references to Heading and then say Heading.element. I know we're going to remove that, but I just want to make sure we're not failing here. Yeah. So now we have this instance where I can slowly create seams where we update the code. For example, I have backspace here. There's nowhere else I reference this backspace function except here. So that's a good place to introduce a seam.
There's nowhere else I reference this backspace function except here. So that's a good place to introduce a seam. I could instead say Heading.backspace. Now of course we know that's going to fail, as you see there. So we grab this whole thing and bring it on up here. And then I can update this to this, thatElement. All right. Come on back. Give it a run. And I think we should be good.
Give it a run. And I think we should be good. And we are. Great. So, all right, this is looking a little better. Now we can imagine doing things like, well, we want to type. I guess I also have things like getting the inner HTML. Maybe it would be easier if I could just get the text of it, and that would return this.element.innerHTML. So we now have a place to add wrappers like this. We could start with this.text.
So we now have a place to add wrappers like this. We could start with this, this.text. And let's see. Is there anywhere else? We have this one that we'll talk about in just a second, but then we have another one here. So let's say heading.text. And anything else? Yep. A couple more.
Yep. A couple more. Let's get rid of that. And then heading.text. Okay. So this should still be working. These are small refactors. And as I say, it doesn't work. I cannot assign to function call on line 68.
I cannot assign to function call on line 68. Yeah. I haven't done the assignments yet. Sorry about that. Heading.text. I just want to get that first little refactor to work. And it does. Okay. I just want to say heading.text.
Okay. I just want to say heading.text. Oh, there it is. plus equals. That was the issue. So maybe we could say heading.append. Maybe we have a new method there. Let's see. If we took that approach, if we just want to append a single character or a single sequence of text, then we would say this.text should be equal to this.text plus what you gave us.
If we took that approach, if we just want to append a single character or a single sequence of text, then we would say this.text should be equal to this.text plus what you gave us. Wow. Three texts in a row. Never done that before. And you know what? I don't like that. Let's just do it directly. += text. All right.
Well, let's come down. This is the section for typing. So let's introduce a seam. And a seam is just where you insert new code that replaces the old code. So this is the old code. The new code would be to simply defer to heading.type. Of course, right now, there's no functionality there, so it's not going to work as soon as we get to the end. So we have our seam. We'll take this, remove it, bring it on up, paste it in.
So we have our seam. We'll take this, remove it, bring it on up, paste it in. And let's see. Does that work? And it does. Great. Which means this type function is now superfluous. So let's find where we call it. Let's see. We call it here.
Let's see. We call it here. So that would just be this.type. We might get hit by... No, because we have an arrow function. That's fine. And then heading.type. And I think that's good. So if we get rid of that, let's do a little cleanup here. It's going slow, but we're getting there.
So if we get rid of that, let's do a little cleanup here. It's going slow, but we're getting there. Give it another go. And that is still working. Great. So now a heading can backspace, but it can also type new text. Next, I think there's a couple places where we reference the length. Yeah, here's another one. So maybe we should accept a length method that just returns this.text.length. Let's see if that would be useful.
So maybe we should accept a length method that just returns this.text.length. Let's see if that would be useful. So right here, if heading.length does not equal the original length, and then... Here's another one. Small refactors. And that should still work. OK. Next, even things like this. I think that's perfectly reasonable to be included on the heading class. Is the heading cleared?
I think that's perfectly reasonable to be included on the heading class. Is the heading cleared? I'm really checking if it's empty. All right. Empty. Return this.text.length is zero. Now think about it. This would return heading.empty. And that's still going to work. I'm not going to make you watch it.
And that's still going to work. I'm not going to make you watch it. Yes, I am, actually. But we no longer need this isHeadingCleared, kind of an awkward name. So let's come back up, and we can just say if heading.empty, and it allows me to get rid of that. OK. Let's see. Are we still good? Yep.
And then when we're done, rather than incrementing, which isn't quite the responsibility of the heading, I'm instead going to set a timeout where we run your callback after 500 milliseconds. So let's get rid of that. And now let's see where we call heading.type. OK, we only do it in this one location. We have to give that original text that I think we're going to get rid of because it's awkward. But now think about it. This callback can now receive these two lines, like so. So if I scroll on back, with any luck, I think it'll still work.
What if we reversed it? So I could say, if it's not empty, then we can still hit backspace or still call backspace. Otherwise, if it is empty, then we can move on to clearing the interval and typing the new text. Let's see. Does that still work? Yes, it does. OK, so now our init function has all the specifics about our program, and in fact, I could even get these and make them nested within here, and actually the same thing for our heading. Because I think if we did everything correctly, there should be no reference to heading within
get these and make them nested within here, and actually the same thing for our heading. Because I think if we did everything correctly, there should be no reference to heading within the class. Ah, there is, though. You saw this earlier, didn't you? This dot.append, this dot.length. There we go. Just reference the method on the instance. So you have your class, and then you have your init function, and I think we should still be good.
So you have your class, and then you have your init function, and I think we should still be good. Small refactors. But now, yeah, this is still very procedural. Like think about it. If I were to remove all of this, and we were just to, if you were to describe to a friend, well, what does it do? Well, when it runs, we start by pausing for a little bit so the user can read whatever the text currently is. Or you might even say, well, wait a couple seconds, and then clear the heading.
Async/Await with Promises33:45
I'll show you a couple options. We could create our function pause, and if we returned a Promise, so we could return a new Promise that will resolve after however long you give us. So milliseconds, and then just say setTimeout, call that resolve function after the number of milliseconds you gave us. Promises can be a little confusing if you're not familiar with them. Think of them as a way of saying, I promise to inform you when I'm done. Here's an example. Let's say when you were 12 years old, you asked your parents to take you to the mall. Your parents aren't going to pick you up from the mall until you call them, right?
So in this case, when you call the pause function, it's returning a promise. We promise to let you know when we are done. And we let you know by calling this resolve function that the promise gives us. So after, in this case, after two seconds, we will call resolve, and that's our way of calling our mom, saying come pick us up, or continue. Now I could say pause(2) seconds, and then alert('come pick me up'), just as an example here. Have a look. One, two, there it is. That's a promise.
So we get the exact same thing here, one, two, and there's your alert. Okay, so this should help us a great deal. Wait a couple seconds, then clear the heading. Well, let's say right up here, heading.clear. And yeah, we don't have a clear method, do we? No. Let's see. All of that code was still within the init function. Okay. So let's add clear up here.
Okay. So let's add clear up here. And then, well, we could migrate all of this here, but I think we can clean this up a little bit more. So why don't we write some new code? Why don't we say, let's think about it. While the heading is not empty, then hit backspace. That's kind of what we're doing. So why don't we say, while this.length is not empty, then backspace. But don't forget, we're not doing that interval code that we had.
So why don't we say, while this.length is not empty, then backspace. But don't forget, we're not doing that interval code that we had. In fact, I'm just going to comment this out for a moment. If we scroll back up, this is all going to run instantaneously, which means if we run it one, two, it'll disappear all at once. So it's sort of like we want to say, well, wait for a backspace. Wait for what represents a backspace. Let's do the same thing here. await. We have to declare this async.
Perfect. So we're up and running now. And that's a much cleaner implementation. So we're going to have to get to this in a second. But if we scroll back, let's see. This can go at the top. Create our heading instance. Then we're going to pause for two seconds, and then we're going to wait for that heading to clear. And I'm not going to get to this line here until this promise on the clear method has
to clear. And I'm not going to get to this line here until this promise on the clear method has resolved. And that's why we call it await. We await for that to resolve. OK. Next, type the new text. Same thing here. So you want to do heading.type. And then, look, one more time.
So you want to do heading.type. And then, look, one more time. Let's put it up there. choices.index. Type the new text. So let's see what we have to do there real quick. So we're going to make this an async function because we promise to let the caller know when we're done. So grab all of this. But I think we're going to have to change this a good bit.
So grab all of this. But I think we're going to have to change this a good bit. So down here, remember earlier, we were calling this callback that you gave us? We're not going to do that anymore. So I can get rid of all of that. And instead, we will resolve. But this still isn't quite right because of how we implemented it before. Notice, we set a timeout for 200 milliseconds. We append some text, and then we call this function again recursively, which would return another promise.
We append some text, and then we call this function again recursively, which would return another promise. And that's not quite what we want anymore. So with that in mind, why don't we... Hmm. I may have to rewrite this. Let's return one more time. And maybe we'll say, give us some text. And while that text has a length to it, then append the first character of that text. So we're doing it sequentially there.
Oh, yeah, it's going to be right here, because we're technically within a new function. Hmm. Let's do it like this, and then I think we're going to extract a function here. Does that fix it? Wait a second or two. Backspace and types the new text. It works. Okay. So that means all of this can be updated. And if we scroll back down, the only remaining step is to increment the currentChoice, which
So that means all of this can be updated. And if we scroll back down, the only remaining step is to increment the current choice, which was this section here. So a decent refactor there, but I think it's going to make it a lot better. So I can get rid of all of that, right? And in fact, you know what? We can clean this up. Where it often gets tricky is when you're dealing with length versus the index, which is zero-based. So instead, let's just say the current one, the current item is one.
is zero-based. So instead, let's just say the current one, the current item is 1. And then we'll say when we're ready to increment it, accept that, and then update current, and then we'll check. Is current greater than or equal to choices.length? If so, we're at the end. Bring it back to 1. Otherwise, increment it. And let's see if that does the trick. Yeah, I think that's right.
We're swapping out the text? Yeah. And actually, we only need to instantiate the heading once. So we end up with something like that. All right. We're getting there. So let's give it a run. It goes to Laravel, to Programming, to PHP, and then back to Laravel. It works. Great.
It works. Great. Notice how once we described what it should do, the only remaining step was to make the swap function reflect the way you described it. Whereas before, when we were spiking it out, we weren't thinking about that at all. So I think we have one or two more little things, and then we're going to call it a day. Already, I'm liking what I see here. Now one thing we might want to allow is this swap function might be the responsibility of the heading.
Encapsulating Swap Behavior42:13
Now one thing we might want to allow is this swap function might be the responsibility of the heading. And in fact, we could even call it a swappable heading, if you like. I'm keeping it simple, but yeah, that's basically what it is. So if we wanted to try that, maybe right up here, what if we had a method called swap? Let's see. We scroll down. That would call heading.swap. At the moment, we would have to give it the new choice. So choices, although we might get rid of that, too.
At the moment, we would have to give it the new choice. So choices, although we might get rid of that, too. So we would get rid of all of that. All right. Let's see. We're going to come up. We'll accept the new text. I'll paste that in there. We begin by pausing for two seconds. Then we clear the text.
Or really, that would be called headings. Like so. All right. Now we scroll down, create our heading. We then provide the choices. We'll update this. Swappable heading. And if we do everything correctly, we should be able to just say heading.begin, maybe? Let's see. We'll add our method here.
Let's see. We'll add our method here. We know, if I scroll back down, we're going to have to track the current one, right? And in fact, I'll grab that there. So we'll start by calling swap. Swap will wait, then clear, then type the new text. We'll update that to this.headings.current. Like so. Which means I can get rid of that. Yeah.
Which means I can get rid of that. Yeah. I think this will work. We will wait for the swap to complete, then paste that in where we update the current. And then begin again. Now if I scroll back down, let's have a look. We can inline this, so let's get rid of all of that. Let's inline this. We no longer need the current. We just have a helper function called pause, although that could even live on the class.
We no longer need the current. We just have a helper function called pause, although that could even live on the class if you want. Next we create our heading, or we could even inline that, and then call begin. Let's see. How do we want this to look? Something like that? Let's have a look. We clear it to... how many times have you seen this? But I hope it should work.
We clear it to... how many times have you seen this? But I hope it should work. Ah, text is undefined on line 65. While text.length. So at some point when we call the type function, text is undefined. It's going to be this thing here again. No, that's right. We call swapHeadingsCurrentCurrent-1 to make it zero-based. That's got to be it. Give it a run.
That's got to be it. Give it a run. I think we're good now. Laravel, programming, php, and then it should go back to Laravel and start over. All right, we're up and running. So here phpStorm is squawking because this function is called recursively because that's what we want. This goes on and on and on. It won't stop. Now, if you want, one option would be this.
It won't stop. Now, if you want, one option would be this. We could say while true, then await this swap, and then increment the current, and then just continue on the loop. This would be another way to handle this. And you'll get the exact same thing. So this can be useful when you want something to continue forever for the entire request. All right, we're going to make our final little tweaks, and we're going to call it a day. First pause. I think that could be a helper function, but why don't we make it live here since it gets
First pause. I think that could be a helper function, but why don't we make it live here since it gets used so much. So let's have an async function, and why don't we even call it wait. I'll paste that in like so. Now up here, I could say this.wait for 100 milliseconds, this.wait, and do we have any other? There's one. This.wait. All right, so that takes care of that.
So again, these are all small refactors trying to remove indentation and assign a name to these pieces of functionality. So notice how much easier this is to take in. Before we had that Mighty Ducks flying V. We had a bunch of callback hell. We don't have any of that anymore. So the only remaining refactor I'm looking for is right here. So in general, I'm looking for indentation. I'm looking for conditionals that aren't very clear. And I'm looking for long lines. This I think is the longest line we have here, which probably means we need to change it.
Paste that in. And that helps. We've assigned it a name, but we still have that long line. But here's the thing. Now that it lives within its own method, we can bring it back to what we had at the beginning, which was obviously the easiest one to understand. We say current, increment it, and then we could say, well, now if current is greater than the heading's length, then bring it back to one. And that should fix it. It's no longer intimidating.
What are we really doing? Type the next heading. OK, well, I see type and I see heading, but it doesn't tell me next. So what if we right here just had nextHeading? And that's going to return what you have here. So notice we're finding confusing pieces of code and we are assigning a name. So we took that approach. I could then say this.nextHeading. And now the comment and then the code is redundant, are redundant. Now what we could even do, do we want to merge these?
And now the comment and then the code is redundant, are redundant. Now what we could even do, do we want to merge these? What we could do is say let next or let heading is this, and that's what we're going to return. But then here we could also handle going to the next one because we fetched the current heading. So now let's move on to the next one, which would mean, and now this ends up being very simple. But what we could do is change. So for example, I could say that is now increments. And then finally, just a little bit of cleanliness.
So for example, I could say that is now increments. And then finally, just a little bit of cleanliness. So let's have a look. This, of course, would go into your class. So let's say script or into a file script/source/js/app.js. Let's create that now. js/app.js or a module, you know, if you want. And now if we come back, we'll keep it right here. And we're setting up a new swappable heading. We give it the H1 we care about as well as the headings.
