A comprehensive guide to using Chrome DevTools debugger for JavaScript debugging with step-by-step examples.
Edit me

Overview

Debugging is an essential skill for developers, and using a debugger can make finding and fixing bugs much faster and more efficient. In this guide, we will cover how to use a debugger in your development environment, walk through some basic functionality, and provide real-life examples for common debugging scenarios.


What is a Debugger?

A debugger is a tool that allows you to inspect and control the execution of your code. It provides features like setting breakpoints, inspecting variables, stepping through code line by line, and analyzing the program’s flow.

By using a debugger, developers can:

  • Pause the execution of code at specific points (breakpoints)
  • Examine variables and objects to understand their values
  • Step through code one line at a time to understand the flow
  • Diagnose performance issues or logical errors

How to Use the Debugger in the Browser (Chrome DevTools)

When debugging JavaScript in the browser, Chrome DevTools provides a powerful suite of tools to inspect, control, and step through your code. In this guide, we’ll explain each debugging step in detail, and then walk you through a hands-on example that includes asynchronous code (e.g., using setTimeout or fetch). This example will allow you to visually see how stepping through code works in real-time, including asynchronous behavior.


Understanding DevTools Debugging Features

Before diving into the code example, let’s familiarize you with some key debugging features that will be useful:

Browser Deugger

Breakpoints

A breakpoint pauses your code execution at a specific point so you can inspect values, variables, and the call stack. Breakpoints are set by clicking on the line number in the Sources tab.

Stepping Through Code

Once the code hits a breakpoint, you can move through the code using several buttons:

Browser Deugger

Resume (▶️)

  • Action: Resumes code execution after it has been paused at a breakpoint.
  • Use Case: After hitting a breakpoint, click this to allow the program to continue running until the next breakpoint or until the script finishes executing.

Step Over (⬇️)

  • Action: Executes the current line of code and moves to the next line.
  • Use Case: If the current line of code is a function call, this button will execute the function but will not step inside it. It will move to the next line after the function call.
  • Example: If you’re inside a function and want to skip stepping into another function within that line, you use this button.

Step Into (⬇️)

  • Action: If the current line of code contains a function call, this button will take you into the function to debug further.
  • Use Case: When you want to debug a function that is being called in the current line of code, click this button to step inside the function and inspect its execution.

Step Out (⏏️)

  • Action: Once inside a function, this button will allow you to exit that function and return to the calling code.
  • Use Case: If you’re inside a function and want to finish execution of that function and return to the code that called it, click this button.

Step (↕️)

  • Action: This button lets you step through the current line of code, including stepping into any async calls or callbacks, so you can track the code execution flow in finer detail
  • Use Case: When you want to break into each step of the code (including async code) to observe how things are executing line by line.

Disable Breakpoint (⛔)

  • Action: Disables all breakpoints in the current script without removing them.
  • Use Case: If you no longer want the script to pause at any breakpoints during this session but want to keep them in place for later debugging, click this button to temporarily disable all breakpoints.

Other Helpful Features

  • Resuming Execution: After pausing at a breakpoint, you can use the Resume button to continue code execution until the next breakpoint or until the script ends.
  • Async Callbacks: Handling asynchronous code (like setTimeout, Promises, or fetch) in debugging requires special attention, as the code doesn’t run sequentially. You’ll need to handle execution flow across multiple time-based or event-based callbacks.
  • Watch Expressions: You can add specific expressions (like user.name) to watch and monitor as you step through the code.
  • Scopes: This panel shows the current local variables and their values. For debugging complex code, it’s essential to monitor variable values as you step through.

Step-by-Step Debugging Process Overview

1. Open DevTools and the Sources Tab

  • Right-click on the page and select Inspect, or press Ctrl+Shift+I (Windows/Linux) or Cmd+Option+I (Mac) to open DevTools.
  • Navigate to the Sources tab.

2. Set a Breakpoint

  • In the Sources tab, find the script you want to debug (this might be a .js file or an inline script).
  • Click on the line number where you want to add the breakpoint.

3. Inspect Variables

  • When the code execution pauses at the breakpoint, use the Scope section to inspect variables and their values.

4. Step Through the Code

  • Use the Step Over, Step Into, and Step Out buttons to navigate through the code and observe the flow of execution.

5. Handle Asynchronous Code

  • If the code contains asynchronous operations (e.g., setTimeout, fetch), watch how the execution flows when these operations complete.

6. Resume Code Execution

  • After inspecting the code, use the Resume button to let the code continue execution.

Example Code with Asynchronous Operations

Here’s a more useful example that includes both synchronous and asynchronous operations. You can use this code to watch how stepping through and inspecting variables works, especially when asynchronous operations (like setTimeout) are involved.

function fetchData(callback) {
  setTimeout(() => {
    console.log("Data fetched!");
    callback("Fetched Data");
  }, 2000);
}

function processData(data) {
  console.log("Processing data:", data);
}

function main() {
  console.log("Starting to fetch data...");
  
  fetchData((data) => {
    processData(data);
  });
  
  console.log("Fetching data initiated...");
}

main();

Explanation of the Example Code

  • fetchData(): This function simulates an asynchronous operation, like fetching data from a server, using setTimeout. It takes a callback and, after 2 seconds, calls the callback with a string (“Fetched Data”).
  • processData(): This function processes the data fetched by fetchData. It simply logs the data to the console.
  • main(): This function is the entry point. It calls fetchData to fetch the data, then proceeds to initiate the fetching process while still logging a message. The asynchronous nature means “Fetching data initiated…” will be logged immediately, but the data processing will be delayed by 2 seconds.

Step-by-Step Debugging Walkthrough

1. Set a Breakpoint in main()

  • Open DevTools and go to the Sources tab.

  • In the File Navigator, locate the script containing the main() function.

  • Set a breakpoint on the first line of the main() function (console.log("Starting to fetch data...");).

2. Reload the Page and Hit the Breakpoint

  • Reload the page to start running the code.

  • DevTools will pause at the breakpoint in main(). The Code Viewer will highlight the paused line in yellow.

Browser Debugger

3. Step Over the Synchronous Code

  • Now, use Step Over (the arrow pointing over) to move through the synchronous code.

    • Click Step Over. The console.log("Starting to fetch data...") will execute, and you’ll see this message in the browser’s console.

    • Use Step Over again to move past the fetchData() call and continue to console.log("Fetching data initiated...").

    Browser Debugger
    • Notice that this message is logged immediately, without waiting for the asynchronous operation to complete.

4. Inspect the Call Stack and Variables

  • Check the Scope panel to see the current variables. The data variable won’t be set yet because the callback hasn’t run. You will see that callback is a function reference, but no data has been passed yet.
Browser Debugger

5. Step Into Asynchronous Code (setTimeout)

  • Now, let’s focus on the asynchronous behavior. The setTimeout function in fetchData() is delayed by 2 seconds.

  • Click Step Into when you’re on the fetchData line. This will take you inside the setTimeout function.

  • After 2 seconds, the callback will be invoked. The debugger will pause again at the callback function. Now you can inspect the data passed to processData(). The value “Fetched Data” will appear in the Scope.

Browser Debugger

6. Step Over Inside the Callback

  • After the callback runs, you can use Step Over to move to the next line, which will call processData(data).

  • The console.log("Processing data:", data) will be executed, showing the fetched data in the console.

Browser Debugger

7. Resume Execution

  • Click Resume to let the script continue running. The rest of the code will execute normally, and you’ll see all the console logs and data being processed.