Mastering Chrome Developer Tools, v4

Using the Script Debugger

Jon Kuperman
Bloomberg
Mastering Chrome Developer Tools, v4

Lesson Description

The "Using the Script Debugger" Lesson is part of the full, Mastering Chrome Developer Tools, v4 course featured in this preview video. Here's what you'd learn in this lesson:

Jon demonstrates how to set breakpoints, use conditional breakpoints, and navigate through the call stack. He also explains how to use the watch panel to inspect variables and the scope panel to see the variables available at a specific point in the code.

Preview
Close

Transcript from the "Using the Script Debugger" Lesson

[00:00:00]
>> Jon Kuperman: So, I go over here to my pages and I go to Lessons, Step Through Debugging. Okay, so we're gonna be spending quite a bit of time in the Sources panel, cuz that's where you do the step three debugging. Let me close all this stuff out, just so we're all on kind of a same even playing field.

[00:00:16]
And if I look in the console, I can see that I definitely have some stuff that's logging to the console saying hello, 'cause I'm very creative. So we go into sources, and on the left side we see all of the assets that came down during that page load.

[00:00:28]
So you can see my local host application here, and you can see it sent down CSS, JS and a lesson. The lesson is HTML, so if I click through here. The JS is some code [INAUDIBLE] here. And the CSS is that style.css, so kind of what we'd expect to see.

[00:00:44]
So let's spend our time inside the JS file, and I tried to keep it quite simple. We've got a set interval, and it runs every 500 milliseconds. And it sort of does this thing where it's got functions calling other functions because I thought that would be a nice way to visualize things.

[00:00:59]
The interval itself calls function 1, function 1 calls function 2, function 2 then calls a little bit of a split. It does like a math random, and if it goes one way, it calls 3, goes the other way, it calls 4. We feel all right about that, it's just basically a few functions calling each other.

[00:01:13]
So, we can do some kind of cool things, we could set a break point here on the if check. And if I hit play it'll wait till another five hundred milliseconds kinda goes through. And every time it's about to execute this code, it pauses. And the pausing will gray out the screen.

[00:01:29]
It'll put this little kind of yellowy, white pause in debugger symbol at the top. And it'll sort of activate this right sidebar. So for now, we don't really need this left sidebar anymore, this was just for kind of selecting the file. So I'm gonna go ahead, and I'm gonna kinda hide it away.

[00:01:43]
So we've got our source code here, and we've got the kind of step through debugger over here on the right. And again, I'm just gonna kinda minimize everything. So to try to make it not as super overwhelming. So this is kind of our minimal case. We're paused on a line that we paused on.

[00:01:57]
The application is not running, it is paused right now. We've got all these different options for things to inspect. And we've got our stepping options, right? So we've got play and stop. We've got step over, we've got step into, we've got step out of, and then we've got the generic step, right?

[00:02:15]
And these basically do the same kind of thing. We're paused here, if we were paused, and we wanted to step into function 3. We could click step into, if we wanna step over function 3, we could click step over. If we just wanna go to the next line, we can click step.

[00:02:27]
And if we're done with this, we can just kinda hit play, and play it through. So some other really interesting things that come here though are while you're paused in this execution context, you actually get access to everything the app would have access to at this point. So for an example, is we can see in this function that it gets this variable passed in foo.

[00:02:45]
And we can see this nice, little handy thing, Chrome Dev Tools tries to fill things in for you. So it actually shows you the value of random number, the value of foo. You'll notice, if I hit play again, it'll be a different random number because it's generated again.

[00:02:57]
So you can see that the random number changes each time. But let's say it wasn't able to give us these things in such a nice way. We can expand this watch panel. And so, what this watch panel will be is it allow you to add new things, and you can add anything in the scope of your application.

[00:03:12]
So you could add anything from the global scope, or from a contained scope, or from this scope, like foo, and it'll show it. You can try to evaluate things, like bar that we don't have, and it'll show bar is not available, we don't have that. So this is, again, all context aware, and kind of to prove that if we go up here, and we pause here now we haven't executed this line yet, so foo doesn't exist at this point.

[00:03:32]
So we can see the watch has updated now foo is undefined. So that sort of makes sense. So it is highly context aware from where your stopped. The watch will re-evaluate everything you're stopped at, and kind of give you what it has access to. We move forward, now foo is defined.

[00:03:48]
So, another really nice thing that we can see is the call stack, which is basically like you paused execution here, but how did we get here? And so for a contrived example like this, it's quite easy. We can see we're in function 2, and it highlights we came from function1.

[00:04:01]
Here's where function1 called function2. That came from inside this anonymous function here where it calls function1. But for a real web application, this can be really valuable, where you know where you are, but you really wanna see, from my full app load, how did I get into this code base?

[00:04:15]
And again, as you click through the call stack, you'll notice the watcher updates, right? Because that watcher re-evaluates. So here, foo was not defined, here foo was defined when it was called, and here foo is still defined. So this can be a really nice way to pause the application.

[00:04:31]
Remember I said, stepping is not time traveling, but the call stack walking is time traveling in this sense, right? Because it's captured that whole session. So if foo was not defined here, defined here, not defined here, defined. You can go back and forth through the call stack. You just can't step backwards in the debugger.

[00:04:47]
So yeah, so this is great. And then the last thing I kinda wanna point out for this section is scope. So, I know there's a lot of fantastic courses on Frontend Master, how JavaScript works as a language and how scope works. And we've got these sorts of different options with our VARs and our lets and cons.

[00:05:02]
We've got this function scope, all this stuff you can all learn on a separate course. But the cool thing here is that these Dev Tools put together where you are in the application. You're inside a function, inside a function, and they give you access to everything you'll have access to at that line.

[00:05:18]
So you can see that two things that we have access to are foo which came past in, and the Global Window object, which has all this stuff on it. But in a more advanced application, you could get a really nice insight here into what you had scope access to.

[00:05:31]
Does that feel all right for people? Basically, it's kind of like putting together variables you would have access to at that point in time, in the code base. Some other stuff to cover is this idea of, so we've got these breakpoints, we've got a few different ways to set them.

[00:05:45]
So I'm gonna go ahead, and I'm gonna unset them by clicking on them here, and play through the application. So one way to set them is to come into this Sources tab, and to click on a line number, and that'll set a breakpoint. And when that breakpoint hits, it will pause there, and I'll deselect it there.

[00:06:00]
Another way that you can get there, which might be easier with large applications, is you can set them with this magical debugger statement. So if we go into debugging.js.
>> Jon Kuperman: And we are to come here, we can go ahead and start a new line and write this debugger statement.

[00:06:17]
So, these debugger statements will be a no op If the dev tools are closed, they won't run at all. But if the dev tools are open, it'll add a break point there, and it'll bring your user there. Now, lint for these, because you can deploy these to production, and not know about it until somebody happens to have their dev tools open, and it'll just add these break points.

[00:06:34]
So, now if we come back, we refresh the page, we see there's a debugger, and then as the timer fired, it stopped at that debugger.
>> Student 1: That worked in production.
>> Jon Kuperman: Yeah, so the debugger word, if it makes it into your production bundle, and any user has the dev tools open, it will stop there so- [INAUDIBLE] But if it's not open, it's okay, yeah, yep?

[00:06:56]
[INAUDIBLE]
>> Jon Kuperman: Yeah, yeah, all the linters have built-in rules to catch debug statements. Yeah, there's built-in rules.
>> Student 2: Does it do anything other than a break point, or is it-
>> Jon Kuperman: No, it just pauses execution.
>> Student 1: It's not really that dangerous.
>> Jon Kuperman: It's not the end of the world at all, it just can be kind of a goofy thing.

[00:07:15]
It's more gonna affect your co-workers than your users, right, where they're gonna be every time I try to open Chrome now I get stopped at this random. I was like, yeah, I committed a debugger, sorry. Yeah, it's not a security problem, it's just a kind of funny annoyance.

[00:07:26]
So let's go ahead and remove this. And then, I wanted to show you one other one, which, again, doesn't come up super often, but I find when it does come up, it's extremely valuable. So you can go ahead, and set a break point, and it'll stop every time we hit foo.

[00:07:38]
And let me go ahead and play this through, but you can set conditional break points too. So I can go ahead, and I can right-click on this, and I can edit the break point. And so this comes up for me a lot, this math example is contrived, right?

[00:07:51]
It's either gonna be more than 0.5 or less than 0.5. But there's lots of cases in an application where you have a helper foo, I use this all the time. Let's say you have a Fetch wrapper, and your whole app uses the fetch wrapper. Every time you make a HTTP request, you use my custom fetch, and you pass in a URL.

[00:08:06]
And you really wanna add a breakpoint, but you only wanna add the breakpoint when you hit this new endpoint that you've created, right? And so you could do a custom breakpoint, and you could say the condition would be, if URL is equal to mycustomendpoint.com, something like this. So you can set these in, where you have these helper functions that fire like a million times.

[00:08:25]
But you only wanna stop in a certain case, these conditional breakpoints can be great. For our case we could say, what if you only stop if random number is greater than 0.7, or something like that. And then basically you can go ahead and hit play, and this will run for quite a while, until eventually the random number is 0.89, and then it stops.

[00:08:46]
But can you see how that would be useful in an app with like helper functions to kind of stop it there? So the conditional break points are really great. And then the last thing I just wanted to cover is that you can go even further with these. You can set, like a fetch break point, right?

[00:08:59]
So again, with my example earlier, instead of conditional breakpoint, you could add a new fetch one to say, okay, stop if we make an HTTP request, but only if the URL contains the word feed, or timeline, or something like that. And it'll do the same thing there. You can do similar things with the DOM break points, which is what I showed earlier, where you can right-click, you can add a break point.

[00:09:18]
You can say break on one of these things. All of that will eventually end up in this Sources panel, where you can see all the different break points that you have. Yeah, so I think at a very high level, that's kind of the idea here is that you've got your stepping logic.

[00:09:33]
So again, if we stop over here at function 1, and we kind of wanna see what goes on. We can go ahead and step into function 1, and we'll get in here like we would expect. And then, if we were to step out of function1, we would go to the end of the file because there's no time traveling.

[00:09:47]
So function1 will call it subfunctions. But as far as we're concerned, we're done. If we hit play again, and we click step over function1, we miss everything else, because we've stepped over, we get to the end of the function call. So we can hit play again. We can go into function 1, then we can go ahead and go 1 next, this is just a variable assignment.

[00:10:04]
We can see this calls function 2, that one I wanna step into, so we step into function 2, and we can kind of move through our codebase. Now, important thing, this works with multiple files, so this example is a bit silly, contrived because it's all here. But if you have a giant codebase with hundreds of different files, this is a very easy way to get into the actual source code that's causing whatever it is you're looking for.

[00:10:22]
>> Student 2: Do you have any tips for using the debugger with minified or transpiled code?
>> Jon Kuperman: Yeah, that's excellent. So, I think that truthfully, the step through debugger will be the most useful when you have source maps, right? So it'll be the most useful when you're either in debug, local mode.

[00:10:39]
Building, or if you build a production, or maybe a CI build with source maps attached. So if source maps are there, then it will run everything you do here through the source map, and it will show you the unminified, uncompiled code. That being said, sometimes you're gonna have an issue on production that you're just trying to get through, and it can be quite difficult with the debugger.

[00:10:59]
One thing I will add that could help a little bit is if you go to the Settings cog, and you go to this ignore list, you can add some ignore exclusions for, let's say, common library code. So if you're really stuck on something gnarly in production code, you could ignore.

[00:11:16]
Well, I know it's not a react bug, so ignore react.js, I know it's not in Lodash, ignore that. So you can kind of narrow it down and eventually, your breakpoints and your step-through will only go through your application code, which can make it a bit easier. But I would say, if it's too difficult in production, if there's any way you can get source maps enabled, even to enable them for production for a few days while you work on this could be great.

[00:11:38]
Because all of these runs through Google DevTools, source maps, so it will always apply them, yeah.
>> Student 2: Does this show between extensions as well as your production code?
>> Jon Kuperman: This will just show the extensions as well. It will, we will stop an extension code sometimes, like another area where you can get kind of bit where you're like, man, my password manager is the thing that called that.

[00:12:00]
>> Student 2: They can read each other.
>> Jon Kuperman: Yeah.
>> Student 2: So you're looking at the whole picture of everything on the page. [INAUDIBLE] and everything.
>> Jon Kuperman: Exactly, yep.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Start a 7-Day Free Trial