Introduction to Dev Tools, v3

Step Through Debugging

Jon Kuperman

Jon Kuperman

Introduction to Dev Tools, v3

Check out a free preview of the full Introduction to Dev Tools, v3 course

The "Step Through Debugging" Lesson is part of the full, Introduction to Dev Tools, v3 course featured in this preview video. Here's what you'd learn in this lesson:

Jon demonstrates how to use the step through debugger in DevTools to pause the execution of an app on a specified line of code. This segment covers how to set and activate breakpoints, the debugger keyword, walk the call stack, conditional breakpoints, and setting XHR breakpoints.


Transcript from the "Step Through Debugging" Lesson

>> All right, so we've kind of made it through the editing section, so we've learned how to do quick edits, we've learned how to do sources, and we've learned how to do workspaces. And now we're gonna move into the debugging section in the workshop. And so, this one will be different where we won't be altering any HTML or CSS.

We will be altering some JavaScript but more importantly, we're gonna be looking at broken things and trying to get a better idea of why they're broken or how they're broken. So, one concept that's great is step through debugging, and I find the room is usually kind of split on this.

Where some people are super familiar with it, and maybe they've even done it before or at least they understand the idea. And other people, especially people who haven't programmed in other languages aren't really sure what it is. And so the idea is that a step through debugger, so when you put a console log in your code, which is how a lot of people debug JavaScript.

The code will run and then it'll hit the console log, log it out, and then it'll keep running the application state. So a contrived example of where that has shortcomings would be, you console log the link that you're about to do an API call to and it's wrong.

And then you're like well wait, what set it to the wrong thing, but it's too late, the application states already finished. Then you'd have to refresh the whole thing or work through the whole workflow again, in order to get back into that application state. And the contrived example of a link being set wrong might not be great, but if you can imagine an app with a really big workflow.

Like you have to go to the site, you log in, you add something to your shopping cart, you go to the coupon page, you enter the coupon, that's where the bug is. So if you console log something, and then you get the wrong value or not value we're expecting, now you have to go manually through that whole step again to get it.

That differs a lot from step through debugger, so a debugger statement unlike a console log, will actually completely pause the application execution, it will not go forward until you tell it to. And so, that can be a lot more powerful because instead of just getting your console log right, you can pause and you can spend as long as you need.

Looking around, changing things, walking back, walking forward, again, you can really just pause the whole app and look around. So, I guess the way I usually think about it is like, console log is great for a lot of tasks, like straightforward tasks, like if you're just like, what's the value of my post ID or something like that.

There's no need to use a debugger for that, you can easily just console log it, but when something's really wrong and you wanna take your time and really explore it. Or when you've console logged three times and you keep getting the wrong thing, there might be a really good time to use a debugger instead and actually poke around, cool.

So, this lesson is pretty short, but it's a little bit heavy, so I'll kinda go through it and then we'll take as long as we need for any questions before we go into the exercise. And feel free to have me go back through things, anything that's helpful. So, I'll go ahead and I'll right click, and I'll click Inspect, and we're gonna be in the Sources tab this time.

So let me close all of these, so if I first go to Console before we get to Sources, we can see I have this function just for demonstration purposes that's just logging in the background, it's just an interval, and it's just logging things out. And so, if we click over here, we can go to this Debugging js file, and we can see I tried to make the whole workshop not very js heavy.

So the basic idea is a set interval which we'll get called regularly, getting called every ten seconds. And it does this code where it's just got a couple things, so it calls function1. Function1 is here, function1 sets a constant then calls function2, there's an if else, then it calls either function3 or function4, that's like the whole thing, again, the JavaScript is not very important.

So if I wanna figure out what's going on, let's say, I wanted to see if function3 gets called or function4 gets called, I'm not sure. I can start at the very beginning and you can really go anywhere you want. And if you hover over these numbers here, you'll see a little gray bar up here, and if you click on one, it'll turn blue, and that means a breakpoint is set.

At the same time, you can see over here on the right sidebar, it keeps a list of all the breakpoints that you have currently, and you can see when is set. So the ten seconds have gone by, so the function gets called, and here we pause. So you can prove it's paused in a lot of different ways but, for an example, you won't see any more console logs here, because it's not getting called over, and over again, anymore, it's been fully paused.

And if you had a more robust website with application state doing things, you would notice it was paused as well. So when something is paused, we use the left sidebar we're using for Workspaces, now we can kind of move that away cuz it's not super important. And then the center panel with our source code, we usually use that to set one or multiple breakpoints.

But when those are set, then it becomes less important and the right sidebar becomes the important thing that we're looking at. So let me minimize all these and we can kind of go through because they're all very different, and we'll actually go bottom up. So, all the ones here are just different break points, right?

So we learn how to do a DOM Breakpoint, in one of the earlier lessons, that was when you right click on an element in the elements path panel. So if we were to go here, right click on body, Break on, subtree modification. Now we're to come back to Sources, we would see that there's now a DOM Breakpoint in here.

So this is just keeping a list, cuz if you're debugging something really hairy, eventually you'll probably have breakpoints all over the place, and it's kinda nice to be able to have a central place for it. So I'm gonna go back to elements, and I'm just gonna remove this breakpoint, cuz we're not using it.

Back to sources, XHR Breakpoints are really interesting, we can cover those in a bit. But the basic idea with XHR Breakpoints is that if you have an app, this used to happen to me all the time at Twitter, so we have, and it's doing constant API calls, right?

It's getting your timeline, and it's getting your friends, and it's getting your following list, and who to follow, it's all these different things. And all have that would be wrapped in a single function, right? Like a helper function like fetch or Twitter API or something like that. And so, there'd be a problem with one of them that I really wanted to debug.

But when I put a breakpoint in the helper function, as you can imagine, it would pause every single time, and so I'd be clicking play, waiting for my one to come. So what you can do is these XHR Breakpoints, you can click plus here, and it's really nice.

It says break only when the URL contains, and so you could do tweets or something like that or at Adobe, you could call it, do something that's only the Photoshop service or something like that. And then the helper function you don't need a manual breakpoint in, it'll actually tell you when an XHR or a fetch request happens with that in the URL.

That can be really helpful, again, it's very powerful but only in niche situations, right? If you have too many function or too many network calls, and you really wanna breakpoint on only one of them, you can use those. But for mostly today, let me pause this, for most today we're just gonna be in the regular breakpoints, and this will be a list of things where you have actually clicked.

So if I go through and I click a bunch of stuff, you can see this list is populating. Go back down to just zero here, cool, then we'll see Scope here, so this is really nice. So, if you've done any of the great Frontend Masters courses on advanced JavaScript or anything like that, you know there's a lot of talk about scope and how JavaScript variables are scoped.

Var versus const scoping and this scoping, and all this different stuff like that. This can be a really nice way to see what this is set to, currently undefined, and what scope you're in for the different functions, like what's in scope. So you have the local scope, which is the set interval, and in it, you have access to four top level things, function1, function2, functions3, function4, and this is undefined.

Then you have the Global scope, right? And that's gonna be the window in this sense, again, not a JavaScript class, but it could be really cool to go back and rewatch one of Kyle Simpson's JavaScript classes. And while he's doing the examples, open up this sources panel with your own example and actually be able to just see everything that you have access to in your Local and your Global scope.

So this will just be all the stuff that's on the window object, right? So we'll go ahead and close that, close that, and close that. Now the Call Stack, right now you can see the Call Stack is one line item long. Because this is in the global scope, there's a single anonymous function, which is this anonymous function right here, which is this one, and that's the whole call stack.

But as we go further stepping in, it can be really good as the Call Stack expands to see, watch it expand basically. And this kind of ties in earlier a lot of people were talking about how real world apps often use frameworks, they often use helper functions. And so, putting a simple debugger or an HTML DOM Breakpoint, might not get you exactly where you need to be.

Because that event listener that DOM breakpoint it's being done by a helper function or it's being done by React or Angular. And so, this Call Stack is really great because while it will start with Angular React, something like that. It will end up being really long and you can back trace all the way through until you see a file name that you recognize, right?

So, often it'll look like react.js, and then eventually it'll be my helper component.js, and then eventually it'll be current debugging view.js. I'm like, that's the one, so you can click on that. You can also do a really cool thing with these, when you're using frameworks and things like that.

Which is you can find any script that's like a third party library, and you can right click on it, and you can click Blackbox script. And what that'll mean is your basically, I don't wanna see that in my call stacks anymore, and I don't wanna see it on my breakpoints anymore.

So, I really recommend that if you're, again, if you're using one of these UI libraries, React or Vue or something like that, and you know the bug is not in the React library itself. So you don't wanna see 30 call stacks of React for your two call stacks of your application.

You can right click on react.js and blackbox it, and then it won't show up here anymore. And then Watch is really cool, basically, you can add watch expressions at any time and we'll do this in a minute to keep an eye on any variable as things move through.

So the equivalent of like, let's do this as an example, so the equivalent of going into our code here. So if we go into debugging.js, and what we wanna do is we wanna, what we used to do would be we would console.log(foo), right? That is a common thing we would do, obviously, it is just set to a string here.

And then you would, let us say, you would hit play on it or remove this breakpoint, you would see when it gets called for the first time, it will set foo and then it will console log it, right? That is like a stuff that we do pretty often.

So here it goes, it logs foo and then a log some other thing. So the equivalent in a breakpoint would be to go ahead and delete this console log and save it again. And then put a breakpoint in here, the same line where we added the console log, and then add a watch expression for foo.

And so now it'll wait until it hits the breakpoint at which point, so we hit the breakpoint at which point, the watch expression foo is here, those are equivalent things basically. So you can see, this is like a lot more setup work than console logging. So, only use it when you have a need to use it, but it's also a lot more robust.

So if I go ahead and remove this breakpoint set one back at the beginning, and hit play again, it'll go through and it'll stop here. And so, all of that has just been like what the UI is, so now let's get into what step through debugging means. And step through debugging has to do with these buttons over here.

And so, basically, when you get caught in a breakpoint, and you can always tell up here there's this little thing that says we're paused in the debugger, you have a few options, right? One button is to be like, I don't care about that resume script execution. So, if I hit this, it would go away it would finish cuz I have no other break points.

And then nine seconds later when it triggered again, it would just break here, again, and I could keep hitting play every ten seconds. Some other options that you can step over to the next function call, so if you're on one, you can step over two or you can step into the current one.

So, say I had function1 and function2 and I stopped at function1, step over would send me to function2 and highlight it. Step into actually take me as we can see here, into the guts of function1, so now we've stepped into this function. And so, you can basically walk down or walk back out, any application.

Again, there's something to keep in mind because of how this works. So if you think about the JavaScript execution stack, and it's going line by line, it's executing things. You can always step over something, you can always step into something. But when you step back out, it can't time travel, it doesn't go back in time, it steps out and goes to the next line.

So, if you're here on function1 and I step out, it will not take me back to function1, again, cuz it can't go backwards in time, it'll take me to the end of the function. And then if I hit play, I have to wait for another function when we get called.

Just gonna pause for a second, if I can make that more clear, if anybody has any questions, I'd love to answer those now. Just on execution being paused and the choices that you have to step over into out of or play on. Yeah, the question was I still don't understand what's going on with watch.

And so, watch lets you put any expression in here, so you can put a variable name or a function name or anything like that, and as it gets paused, you can see what it's set to. So let me make an example, let me deselect this and play on just, we get back to the screen and we go into the code here.

So inside function1, we set foo to foo, right? And then we call function2 and we pass foo into it, and then we check if foo is foo, which it will be and we call function3. So let's take function3, we'll pass foo into it, and then we'll receive foo here, and then we'll do foo equals bar, and we'll set that here.

Does that make sense to kind of the JavaScript level? So, we set a variable to something and pass it in, pass it in again, and now we change what the variable is set to. Does that make sense? Okay, so we'll save that and we'll restart, and we'll put our breakpoint back here.

And we'll give it a couple seconds till it gets caught in the breakpoint. Okay, great. So as of this point, we have foo in our watch expression and it has not been defined, so it's unavailable, it would be undefined if you were to console log it. But we'll step into function1, and you see that on this line it hasn't executed yet.

So as we know from JavaScript hoisting, the declaration has been hoisted but the evaluation hasn't happened, so it says foo is undefined. Into the next line function2, at this point foo has been defined, so now it's set to foo. So it's basically, it's like anytime the variable changes, the watcher updates here.

So if we step into function2, we can see that foo is still foo, and then we step in, we can see that we get into this call a function3. Where we pass it in, step in again, and we can see again this line 18 hasn't executed yet. So as of this time, foo is still foo, but when we step into the very next line, now foo has been changed to bar.

So the watcher is like anytime this variable updates, let me know, and that's really important because if we didn't pass it in, then foo would go back to undefined, because it wouldn't be in that scope. But it's basically, the watchers are the same, the watcher is basically the equivalent of wherever we currently are in, we have the blue line is of doing a console log of whatever you put in the watcher at that line.

That's like the same thing, it's just the way of logging out values. Cool, and so, again, so the kind of thing to do when you're debugging code would be to figure out where the problem is. That's always the first thing that you wanna do, like what function has the problem or whatever.

So if you're getting something like is undefined, you're like, okay, and maybe you would see that in your console, you'd see is undefined. And you'd click on the file name over here, and it would take you into the Sources tab where you're calling And then you can put a breakpoint on it, or you can put a breakpoint right after it.

And you could watch all the foo and see what properties foo does have, does it have a bar? No, does it have a baz, maybe does have that those kinda things. So you can really take as long as you need paused in this execution state, and kinda when you're done, you can hit play and it'll play it through.

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