# JavaScript debugging and error handling {{PreviousMenuNext("Learn_web_development/Core/Scripting/House_data_UI","Learn_web_development/Core/Frameworks_libraries", "Learn_web_development/Core/Scripting")}} In this lesson, we will return to the subject of debugging JavaScript (which we first looked at in [What went wrong?](/en-US/docs/Learn_web_development/Core/Scripting/What_went_wrong)). Here we will delve deeper into techniques for tracking down errors and explain how to code defensively and handle errors in your code, avoiding problems in the first place.
Prerequisites: An understanding of HTML and the fundamentals of CSS, familiarity with JavaScript basics as covered in previous lessons.
Learning outcomes:
  • Using browser developer tools to inspect the JavaScript running on your page and see what errors it is generating.
  • Using console.log() and console.error() for debugging.
  • Advanced JavaScript debugging with browser devtools.
  • Error handling with conditionals, try...catch, and throw.
## Recap on types of JavaScript error Earlier in the module, in [What went wrong?](/en-US/docs/Learn_web_development/Core/Scripting/What_went_wrong), we looked broadly at the kinds of error that can occur in JavaScript programs, and said that they can be roughly broken down into two types — syntax errors and logic errors. We also helped you to make sense of some common types of JavaScript error message, and showed you how to do some simple debugging using [`console.log()`](/en-US/docs/Web/API/console/log_static) statements. In this article, we will look more deeply at the tools available for tracking down errors, and also look at ways to prevent errors in the first place. ## Linting your code You should validate your code first before trying to track down specific errors. Make use of the W3C's [Markup validation service](https://validator.w3.org/), [CSS validation service](https://jigsaw.w3.org/css-validator/), and a JavaScript linter such as [ESLint](https://eslint.org/play/) to make sure your code is valid. This will likely reveal some errors, which you can then fix, allowing you to concentrate on the remaining errors. ### Code editor plugins It is not very convenient to have to copy and paste your code into a web page to check its validity over and over again. We'd recommend installing a linter plugin in your code editor, to report errors as you write your code. Try searching for ESLint in your code editor's plugins or extensions list and installing it. ## Common JavaScript problems There are several common JavaScript problems that you will want to be mindful of, such as: - Basic syntax and logic problems (again, check out [Troubleshooting JavaScript](/en-US/docs/Learn_web_development/Core/Scripting/What_went_wrong)). - Making sure variables, etc. are defined in the correct scope, and you are not running into conflicts between items declared in different places (see [Function scope and conflicts](/en-US/docs/Learn_web_development/Core/Scripting/Functions#function_scope_and_conflicts)). - Confusion about [`this`](/en-US/docs/Web/JavaScript/Reference/Operators/this), in terms of what scope it applies to, and therefore if its value is what you intended. You can read [What is "this"?](/en-US/docs/Learn_web_development/Core/Scripting/Object_basics#what_is_this) for a light introduction; you should also study examples like [this one](https://github.com/mdn/learning-area/blob/7ed039d17e820c93cafaff541aa65d874dde8323/javascript/oojs/assessment/main.js#L143), which shows a typical pattern of saving a `this` scope to a separate variable, then using that variable in nested functions so you can be sure you are applying functionality to the correct `this` scope. - Incorrectly using functions inside loops that iterate with a global variable (more generally, "getting the scope wrong"). > [!CALLOUT] > For example, in [bad-for-loop.html](https://mdn.github.io/learning-area/tools-testing/cross-browser-testing/javascript/bad-for-loop.html) (see [source code](https://github.com/mdn/learning-area/blob/main/tools-testing/cross-browser-testing/javascript/bad-for-loop.html)), we loop through 10 iterations using a variable defined with `var`, each time creating a paragraph and adding an [onclick](/en-US/docs/Web/API/Element/click_event) event handler to it. When clicked, we want each one to display an alert message containing its number (the value of `i` at the time it was created). Instead, they all report `i` as 11 — because the `for` loop does all its iterating before nested functions are invoked. > > The easiest solution is to declare the iteration variable with `let` instead of `var`—the value of `i` associated with the function is then unique to each iteration. See [good-for-loop.html](https://mdn.github.io/learning-area/tools-testing/cross-browser-testing/javascript/good-for-loop.html) (see the [source code](https://github.com/mdn/learning-area/blob/main/tools-testing/cross-browser-testing/javascript/good-for-loop.html) also) for a version that works. - Making sure [asynchronous operations](/en-US/docs/Learn_web_development/Extensions/Async_JS) have completed before trying to use the values they return. This usually means understanding how to use _promises_: using [`await`](/en-US/docs/Web/JavaScript/Reference/Operators/await) appropriately or running the code to handle the result of an asynchronous call in the promise's {{jsxref("Promise.then()", "then()")}} handler. See [How to use promises](/en-US/docs/Learn_web_development/Extensions/Async_JS/Promises) for an introduction to this topic. > [!NOTE] > [Buggy JavaScript Code: The 10 Most Common Mistakes JavaScript Developers Make](https://www.toptal.com/developers/javascript/10-most-common-javascript-mistakes) has some nice discussions of these common mistakes and more. ## The Browser JavaScript console Browser developer tools have many useful features for debugging JavaScript. For a start, the JavaScript console will report errors in your code. Make a local copy of our [fetch-broken](https://mdn.github.io/learning-area/tools-testing/cross-browser-testing/javascript/fetch-broken/) example (see the [source code](https://github.com/mdn/learning-area/tree/main/tools-testing/cross-browser-testing/javascript/fetch-broken) also). If you look at the console, you'll see an error message. The exact wording is browser-dependent, but it will be something like: "Uncaught TypeError: heroes is not iterable", and the referenced line number is 25. If we look at the source code, the relevant code section is this: ```js function showHeroes(jsonObj) { const heroes = jsonObj["members"]; for (const hero of heroes) { // … } } ``` So the code falls over as soon as we try to use `jsonObj` (which, as you might expect, is supposed to be a [JSON object](/en-US/docs/Learn_web_development/Core/Scripting/JSON)). This is supposed to be fetched from an external `.json` file using the following {{domxref("Window/fetch", "fetch()")}} call: ```js const requestURL = "https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json"; const response = fetch(requestURL); populateHeader(response); showHeroes(response); ``` However, this fails. ## The Console API You may already know what is wrong with this code, but let's look at how you could investigate it. We'll start with the [Console](/en-US/docs/Web/API/console) API, which allows JavaScript code to interact with the browser's JavaScript console. It has several features available; you've already encountered [`console.log()`](/en-US/docs/Web/API/console/log_static), which prints a custom message to the console. Try adding a `console.log()` call to log the return value of `fetch()`, like this: ```js const requestURL = "https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json"; const response = fetch(requestURL); console.log(`Response value: ${response}`); populateHeader(response); showHeroes(response); ``` Refresh the page in the browser. This time, before the error message, you'll see a new message logged to the console: ```plain Response value: [object Promise] ``` The `console.log()` output shows that the return value of `fetch()` is not the JSON data, it's a {{jsxref("Promise")}}. The `fetch()` function is asynchronous: it returns a `Promise` that is fulfilled only when the actual response has been received from the network. Before we can use the response, we have to wait for the `Promise` to be fulfilled. ### `console.error()` and call stacks As a brief digression, let's try using a different console method to report the error — {{domxref("console.error_static", "console.error()")}}. In your code, replace ```js console.log(`Response value: ${response}`); ``` with ```js console.error(`Response value: ${response}`); ``` Save your code and refresh the browser; you'll now see the message reported as an error, with the same color and icon as the uncaught error below it. In addition, you'll see an expand/collapse arrow next to the message. If you press this, you'll see a single line indicating the line in the JavaScript file in which the error originated. In fact, the uncaught error line _also_ has this, but it has two lines: ```plain showHeroes http://localhost:7800/js-debug-test/index.js:25 http://localhost:7800/js-debug-test/index.js:10 ``` This means that the error is caused by the `showHeroes()` function, line 25, as we noted earlier. If you look at your code, you'll see that the anonymous call on line 10 is calling `showHeroes()`. These lines are referred to as a **call stack**, and can be really useful when trying to track down the source of an error involving several different locations in your code. The `console.error()` call isn't particularly useful in this case, but it can be useful for generating a call stack if one is not already available. ### Fixing the error Anyway, let's get back to trying to fix our error. We can access the response from the fulfilled `Promise` by chaining the {{jsxref("Promise.prototype.then()", "then()")}} method onto the end of the `fetch()` call. We can then pass the resulting response value into the functions that accept it, like this: ```js fetch(requestURL).then((response) => { populateHeader(response); showHeroes(response); }); ``` Save and refresh, and see if your code is working. Spoiler alert — the above change has not fixed the problem. Unfortunately, we **still have the same error**! > [!NOTE] > To summarize, any time something is not working and a value does not appear to be what it is meant to be at some point in your code, you can use `console.log()`, `console.error()`, or another similar function to print out the value and see what is happening. ## Using the JavaScript debugger Let's investigate this problem further using a more sophisticated feature of browser developer tools: the [JavaScript debugger](https://firefox-source-docs.mozilla.org/devtools-user/debugger/index.html), as it is called in Firefox. > [!NOTE] > Similar tools are available in other browsers; the [Sources tab](https://developer.chrome.com/docs/devtools/#sources) in Chrome, Debugger in Safari (see [Safari Web Development Tools](https://developer.apple.com/safari/tools/)), etc. In Firefox, the Debugger tab looks like this: ![Firefox debugger](debugger-tab.png) - On the left, you can select the script you want to debug (in this case we have only one). - The center panel shows the code in the selected script. - The right-hand panel shows useful details about the current environment — _Breakpoints_, _Callstack_, and currently active _Scopes_. The main feature of such tools is the ability to add breakpoints to code — these are points where the execution of the code stops, and at that point you can examine the environment in its current state and see what is going on. Let's explore using breakpoints: 1. The error is being thrown at the same line as before — `for (const hero of heroes) {` — line 26 in the screenshot below. Click on the line number in the center panel to add a breakpoint to it (you'll see a blue arrow appear over the top of it). 2. Now refresh the page (Cmd/Ctrl + R) — the browser will pause execution of the code on that line. At this point, the right-hand side will update to show the following: ![Firefox debugger with a breakpoint](breakpoint.png) - Under _Breakpoints_, you'll see the details of the breakpoint you have set. - Under _Call Stack_, you'll see a few entries — this is basically the same as the callstack we looked at earlier in the `console.error()` section. _Call Stack_ shows a list of the functions that were invoked to cause the current function to be invoked. At the top, we have `showHeroes()`, the function we are currently in, and second we have `onload`, which stores the event handler function containing the call to `showHeroes()`. - Under _Scopes_, you'll see the currently active scope for the function we are looking at. We only have three — `showHeroes`, `block`, and `Window` (the global scope). Each scope can be expanded to show the values of variables inside the scope when execution is stopped. We can find out some very useful information in here: 1. Expand the `showHeroes` scope — you can see from this that the heroes variable is `undefined`, indicating that accessing the `members` property of `jsonObj` (first line of the function) didn't work. 2. You can also see that the `jsonObj` variable is storing a {{domxref("Response")}} object, not a JSON object. The argument to `showHeroes()` is the value the `fetch()` promise was fulfilled with. This promise is not in JSON format: it is a `Response` object. An extra step is needed to retrieve the response's content as a JSON object. We'd like you to try fixing this problem yourself. To get you started, see the documentation for the {{domxref("Response")}} object. If you get stuck, you can find the fixed source code at . > [!NOTE] > The debugger tab has many other useful features that are not discussed here. For example, conditional breakpoints and watch expressions. For a lot more information, see the [Debugger](https://firefox-source-docs.mozilla.org/devtools-user/debugger/index.html) page. ## Handling JavaScript errors in your code HTML and CSS are permissive — errors and unrecognized features can often be handled due to the nature of the languages. For example, CSS will ignore unrecognized properties, and the rest of the code will often just work. JavaScript, however, is not as permissive as HTML and CSS; if the JavaScript engine encounters mistakes or unrecognized syntax, it will often throw errors. Let's explore a common strategy for handling JavaScript errors in your code. The following sections are designed to be followed by making a copy of the template file shown below as `handling-errors.html` on your local machine, adding the code snippets in between the opening and closing `` tags, then opening the file in a browser and looking at the output in the devtools JavaScript console. ```html Handling JS errors ``` ### Conditionals A common use of [JavaScript conditionals](/en-US/docs/Learn_web_development/Core/Scripting/Conditionals) is to handle errors. Conditionals allow you to run different code depending on the value of a variable. Often, you will want to use this defensively to avoid throwing an error if the value does not exist or is of the wrong type, or to capture an error if the value would cause an incorrect result to be returned, which could cause problems later on. Let's look at an example. Say we have a function that takes the user's height in inches as an argument and returns their height in meters, to 2 decimal places. This could look like so: ```js function inchesToMeters(num) { const mVal = (num * 2.54) / 100; const m2dp = mVal.toFixed(2); return m2dp; } ``` 1. In your example file's `