A mostly complete guide to error handling in JavaScript
Tags: beginners, javascript
Published: 2020-09-14 09:24:03

---
title: "A mostly complete guide to error handling in JavaScript"
published: true
description: "Learn how to deal with errors and exceptions in synchronous and asynchronous JavaScript code."
tags: beginners, javascript
canonical_url: https://www.valentinog.com/blog/error/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/653c36jo8p1us3wvnnfu.png
---

## What is an error in programming?

**Things don't go always well** in our programs.

In particular, there are situations where we may want to **stop the program or inform the user if something bad happens**.

For example:

- the program tried to open a non-existent file.
- the network connection is broken.
- the user entered invalid input.

In all these cases we as programmers, create **errors**, or we let the programming engine create some for us. 

After creating the error we can inform the user with a message, or we can halt the execution altogether.

## What is an error in JavaScript?

An **error in JavaScript is an object**, which is later **thrown** to halt the program.

To create a new error in JavaScript we call the appropriate **constructor function**. For example, to create a new, generic error we can do:

```javascript
const err = new Error("Something bad happened!");
```

When creating an error object it's also possible to omit the `new` keyword:

```javascript
const err = Error("Something bad happened!");
```

Once created, the error object presents three properties:

- `message`: a string with the error message.
- `name`: the error's type.
- `stack`: a stack trace of functions execution.

For example, if we create a new `TypeError` object with the appropriate message, the `message` will carry the actual error string, while `name` will be `"TypeError"`:

```javascript
const wrongType = TypeError("Wrong type given, expected number");

wrongType.message; // "Wrong type given, expected number"
wrongType.name; // "TypeError"
```

Firefox also implements a bunch on non-standard property like `columnNumber`, `filename`, and `lineNumber`.

## Many types of errors in JavaScript

There are many types of errors in JavaScript, namely:

- `Error`
- `EvalError`
- `InternalError`
- `RangeError`
- `ReferenceError`
- `SyntaxError`
- `TypeError`
- `URIError`

Remember, all these error types are **actual constructor functions** meant to return a new error object.

In your code you'll mostly use `Error` and `TypeError`, two of the most common types, to create your own error object.

Most of the times, the majority of errors will come directly from the JavaScript engine, like `InternalError` or `SyntaxError`.

An example of `TypeError` occurs when you try to reassign `const`:

```javascript
const name = "Jules";
name = "Caty";

// TypeError: Assignment to constant variable.
```

An example of `SyntaxError` is when you misspell language keywords:

```javascript
va x = '33';
// SyntaxError: Unexpected identifier
```

Or when you use reserved keywords in wrong places, like `await` outside of an `async` function:

```javascript
function wrong(){
    await 99;
}

wrong();

// SyntaxError: await is only valid in async function
```

Another example of `TypeError` occurs when we select non-existent HTML elements in the page:

```text
Uncaught TypeError: button is null
```

In addition to these traditional error objects, an `AggregateError` object is going to land soon in JavaScript. `AggregateError` is convenient for wrapping multiple errors together, as we'll see later.

Besides these built-in errors, in the browser we can find also:

- `DOMException`.
- `DOMError`, deprecated and no longer used today.

`DOMException` is a family of errors related to Web APIs. They are thrown when we do silly things in the browser, like:

```javascript
document.body.appendChild(document.cloneNode(true));
``` 

The result:

```text
Uncaught DOMException: Node.appendChild: May not add a Document as a child
```

For a complete list see [this page on MDN](https://developer.mozilla.org/en-US/docs/Web/API/DOMException).

## What is an exception?

Most developers think that error and exceptions are the same thing. In reality, an **error object becomes an exception only when it's thrown**.

To throw an exception in JavaScript we use `throw`, followed by the error object:

```javascript
const wrongType = TypeError("Wrong type given, expected number");

throw wrongType;
```

The short form is more common, in most code bases you'll find:

```javascript
throw TypeError("Wrong type given, expected number");
```

or

```javascript
throw new TypeError("Wrong type given, expected number");
```

It's unlikely to throw exceptions outside of a function or a conditional block. Instead, consider the following example:

```javascript
function toUppercase(string) {
  if (typeof string !== "string") {
    throw TypeError("Wrong type given, expected a string");
  }

  return string.toUpperCase();
}
```

Here we check if the function argument is a string. If it's not we throw an exception.

Technically, you could throw anything in JavaScript, not only error objects:

```javascript
throw Symbol();
throw 33;
throw "Error!";
throw null;
```

However, it's better to avoid these things: **always throw proper error objects, not primitives**. 

By doing so you keep error handling consistent through the codebase. Other team members can always expect to access `error.message` or `error.stack` on the error object.

## What happens when we throw an exception?

**Exceptions are like an elevator going up**: once you throw one, **it bubbles up in the program stack, unless it is caught somewhere**.

Consider the following code:

```javascript
function toUppercase(string) {
  if (typeof string !== "string") {
    throw TypeError("Wrong type given, expected a string");
  }

  return string.toUpperCase();
}

toUppercase(4);
```

If you run this code in a browser or in Node.js, the program stops and reports the error:

```text
Uncaught TypeError: Wrong type given, expected a string
    toUppercase http://localhost:5000/index.js:3
    <anonymous> http://localhost:5000/index.js:9
```

In addition, you can see the exact line where the error happened.

This report is a **stack trace**, and it's helpful for tracking down problems in your code.

The stack trace goes from bottom to top. So here:

```text
    toUppercase http://localhost:5000/index.js:3
    <anonymous> http://localhost:5000/index.js:9
```

We can say:

- something in the program at line 9 called `toUppercase`
- `toUppercase` blew up at line 3

In addition to seeing this stack trace in the browser's console, you can access it on the `stack` property of the error object.

If the exception is **uncaught**, that is, nothing is done by the programmer to catch it, the program will crash.

**When, and where you catch an exception in your code depends on the specific use case**.

For example **you may want to propagate an exception up in the stack to crash the program altogether**. This could happen for fatal errors, when it's safer to stop the program rather than working with invalid data.

Having introduced the basics let's now turn our attention to **error and exception handling in both synchronous and asynchronous JavaScript code**.

## Synchronous error handling

Synchronous code is most of the times straightforward, and so its error handling.

### Error handling for regular functions

Synchronous code is executed in the same order in which is written. Let's take again the previous example:

```javascript
function toUppercase(string) {
  if (typeof string !== "string") {
    throw TypeError("Wrong type given, expected a string");
  }

  return string.toUpperCase();
}

toUppercase(4);
```

Here the engine calls and executes `toUppercase`. All happens **synchronously**. To **catch** an exception originating by such synchronous function we can use `try/catch/finally`:

```javascript
try {
  toUppercase(4);
} catch (error) {
  console.error(error.message);
  // or log remotely
} finally {
  // clean up
}
```

Usually, `try` deals with the happy path, or with the function call that could potentially throw.
 
`catch` instead, **captures the actual exception**. It **receives the error object**, which we can inspect (and send remotely to some logger in production).

The `finally` statement on the other hand runs regardless of the function's outcome: whether it failed or succeeded, any code inside `finally` will run.

Remember: `try/catch/finally` is a **synchronous** construct: **it has now way to catch exceptions coming from asynchronous code**.

### Error handling for generator functions

A generator function in JavaScript is a special type of function.

It can be **paused and resumed at will**, other than providing a **bi-directional communication channel** between its inner scope and the consumer.

To create a generator function we put a star `*` after the `function` keyword:

```javascript
function* generate() {
//
}
```

Once inside the function we can use `yield` to return values:

```javascript
function* generate() {
  yield 33;
  yield 99;
}
```

The **return value from a generator function** is an **iterator object**. To **pull values out a generator** we can use two approaches:
 
- calling `next()` on the iterator object.
- **iteration** with `for...of`.

If we take our example, to get values from the generator we can do:

```javascript
function* generate() {
  yield 33;
  yield 99;
}

const go = generate();
```

Here `go` becomes our iterator object when we call the generator function.

From now on we can call `go.next()` to advance the execution:

```javascript
function* generate() {
  yield 33;
  yield 99;
}

const go = generate();

const firstStep = go.next().value; // 33
const secondStep = go.next().value; // 99
```

Generators also work the other way around: **they can accept values and exceptions back from the caller**.

In addition to `next()`, iterator objects returned from generators have a `throw()` method.

With this method we can halt the program by injecting an exception into the generator:

```javascript
function* generate() {
  yield 33;
  yield 99;
}

const go = generate();

const firstStep = go.next().value; // 33

go.throw(Error("Tired of iterating!"));

const secondStep = go.next().value; // never reached
``` 

To catch such error you would wrap your code inside the generator with `try/catch` (and `finally` if needed):

```javascript
function* generate() {
  try {
    yield 33;
    yield 99;
  } catch (error) {
    console.error(error.message);
  }
}
```

Generator functions can also throw exceptions to the outside. The mechanism for catching these exceptions is the same for catching synchronous exceptions: `try/catch/finally`. 

Here's an example of a generator function consumed from the outside with `for...of`:

```javascript
function* generate() {
  yield 33;
  yield 99;
  throw Error("Tired of iterating!");
}

try {
  for (const value of generate()) {
    console.log(value);
  }
} catch (error) {
  console.error(error.message);
}

/* Output:
33
99
Tired of iterating!
*/
```

Here we iterate the happy path inside a `try` block. If any exceptions occurs we stop it with `catch`.

## Asynchronous error handling

JavaScript is synchronous by nature, being a single-threaded language.

Host environments like browsers engines augment JavaScript with a number of Web API for interacting with external systems, and for dealing with I/O bound operations.

Examples of asynchronicity in the browser are **timeouts, events, Promise**.

**Error handling in the asynchronous world** is distinct from its synchronous counterpart.

Let's see some examples.

### Error handling for timers

In the beginning of your explorations with JavaScript, after learning about `try/catch/finally`, you might be tempted to put it around any block of code.

Consider the following snippet:

```javascript
function failAfterOneSecond() {
  setTimeout(() => {
    throw Error("Something went wrong!");
  }, 1000);
}
```

This function throws after roughly 1 second. What's the right way to handle this exception?

The following example **does not work**:

```javascript
function failAfterOneSecond() {
  setTimeout(() => {
    throw Error("Something went wrong!");
  }, 1000);
}

try {
  failAfterOneSecond();
} catch (error) {
  console.error(error.message);
}
```

As we said, `try/catch` is synchronous. On the other hand we have `setTimeout`, a browser API for timers.

By the time the callback passed to `setTimeout` runs, our `try/catch` is **long gone**. The program will crash because we failed to capture the exception.

**They travel on two different tracks**:

```text
Track A: --> try/catch
Track B: --> setTimeout --> callback --> throw
```

If we don't want to crash the program, to handle the error correctly we must move `try/catch` inside the callback for `setTimeout`.

But, this approach doesn't make much sense most of the times. As we'll see later, **asynchronous error handling with Promises provides a better ergonomic**.

### Error handling for events

HTML nodes in the Document Object Model are connected to `EventTarget`, the common ancestor for any event emitter in the browser.

That means we can [listen for events on any HTML element in the page](https://www.valentinog.com/blog/event/#how-does-event-driven-applies-to-javascript-in-the-browser).

([Node.js will support](https://nodejs.org/api/events.html#events_eventtarget_and_event_api) `EventTarget` in a future release).

The **error handling mechanics for DOM events follows the same scheme of any asynchronous** Web API.

Consider the following example:

```javascript
const button = document.querySelector("button");

button.addEventListener("click", function() {
  throw Error("Can't touch this button!");
});
```

Here we throw an exception as soon as the button is clicked. How do we catch it? This pattern **does not work**, and won't prevent the program from crashing:

```javascript
const button = document.querySelector("button");

try {
  button.addEventListener("click", function() {
    throw Error("Can't touch this button!");
  });
} catch (error) {
  console.error(error.message);
}
```

As with the previous example with `setTimeout`, any callback passed to `addEventListener` is executed **asynchronously**:


```text
Track A: --> try/catch
Track B: --> addEventListener --> callback --> throw
```

If we don't want to crash the program, to handle the error correctly we must move `try/catch` inside the callback for `addEventListener`.

But again, there's little of no value in doing this.

As with `setTimeout`, exception thrown by an asynchronous code path are **un-catchable** from the outside, and will crash your program. 

In the next sections we'll see how Promises and `async/await` can ease error handling for asynchronous code.

### How about onerror?

HTML elements have a number of event handlers like `onclick`, `onmouseenter`, `onchange` to name a few.

There is also `onerror`, but it has nothing to do with `throw` and friends.

The `onerror` event handler fires any time an HTML element like an `<img>` tag or a `<script>` hits a non-existent resource.

Consider the following example:

```html
// omitted
<body>
<img src="nowhere-to-be-found.png" alt="So empty!">
</body>
// omitted
```

When visiting an HTML document with a missing or non-existent resource the browser's console records the error:

```text
GET http://localhost:5000/nowhere-to-be-found.png
[HTTP/1.1 404 Not Found 3ms]
```

In our JavaScript we have the chance to "catch" this error with the appropriate event handler:

```javascript
const image = document.querySelector("img");

image.onerror = function(event) {
  console.log(event);
};
```

Or better:

```javascript
const image = document.querySelector("img");

image.addEventListener("error", function(event) {
  console.log(event);
});
```

This pattern is useful for **loading alternate resources in place of missing images or scripts**.

But remember: `onerror`, has nothing to do with `throw` or `try/catch`.

### Error handling with Promise

To illustrate error handling with Promise we'll "promisify" one of our original examples. We tweak the following function:

```javascript
function toUppercase(string) {
  if (typeof string !== "string") {
    throw TypeError("Wrong type given, expected a string");
  }

  return string.toUpperCase();
}

toUppercase(4);
```

Instead of returning a simple string, or an exception, we use respectively `Promise.reject` and `Promise.resolve` to handle error and success:

```javascript
function toUppercase(string) {
  if (typeof string !== "string") {
    return Promise.reject(TypeError("Wrong type given, expected a string"));
  }

  const result = string.toUpperCase();

  return Promise.resolve(result);
}
```

(Technically there's nothing asynchronous in this code, but it serves well to illustrate the point).

Now that the function is "promisified" we can attach `then` for consuming the result, and `catch` for **handling the rejected Promise**:

```javascript
toUppercase(99)
  .then(result => result)
  .catch(error => console.error(error.message));
```

This code will log:

```text
Wrong type given, expected a string
```

In the realm of Promise, `catch` is the construct for handling errors.

In addition to `catch` and `then` we have also `finally`, similar to the `finally` in `try/catch`.

As its synchronous "relative", Promise's `finally` runs **regardless** of the Promise outcome:

```javascript
toUppercase(99)
  .then(result => result)
  .catch(error => console.error(error.message))
  .finally(() => console.log("Run baby, run"));
```

Always keep in mind that any callback passed to `then/catch/finally` is handled asynchronously by the Microtask Queue. They're **micro tasks** with precedence over macro tasks such as events and timers.

### Promise, error, and throw

As a **best practice when rejecting a Promise** it's convenient to provide an error object:

```javascript
Promise.reject(TypeError("Wrong type given, expected a string"));
```

By doing so you keep error handling consistent through the codebase. Other team members can always expect to access `error.message`, and more important you can inspect stack traces.

In addition to `Promise.reject`, we can exit from a Promise chain by **throwing** an exception.

Consider the following example:

```javascript
Promise.resolve("A string").then(value => {
  if (typeof value === "string") {
    throw TypeError("Expected a number!");
  }
});
```

We resolve a Promise with a string, and then the chain is immediately broken with `throw`.

To stop the exception propagation we use `catch`, as usual:

```javascript
Promise.resolve("A string")
  .then(value => {
    if (typeof value === "string") {
      throw TypeError("Expected a number!");
    }
  })
  .catch(reason => console.log(reason.message));
```

This pattern is common in `fetch`, where we check the response object in search for errors:

```javascript
fetch("https://example-dev/api/")
  .then(response => {
    if (!response.ok) {
      throw Error(response.statusText);
    }

    return response.json();
  })
  .then(json => console.log(json));
```

Here the exception can be intercepted with `catch`. If we fail, or decide to not catch it there, **the exception is free to bubble up in the stack**.

This is not bad per-se, but different environments react differently to uncaught rejections.

Node.js for example in the future will let crash any program where Promise rejections are un-handled:

```text
DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
```

Better you catch them!

### Error handling for "promisified" timers

With timers or events it's not possible to catch exceptions thrown from a callback. We saw an example in the previous section:

```javascript
function failAfterOneSecond() {
  setTimeout(() => {
    throw Error("Something went wrong!");
  }, 1000);
}

// DOES NOT WORK
try {
  failAfterOneSecond();
} catch (error) {
  console.error(error.message);
}
```

A solution offered by Promise consists in the "promisification" of our code. Basically, we wrap our timer with a Promise:

```javascript
function failAfterOneSecond() {
  return new Promise((_, reject) => {
    setTimeout(() => {
      reject(Error("Something went wrong!"));
    }, 1000);
  });
}
```

With `reject` we set off a Promise rejection, which carries an error object.

At this point we can handle the exception with `catch`:

```javascript
failAfterOneSecond().catch(reason => console.error(reason.message));
```

Note: it's common to use `value` as the returning value from a Promise, and `reason` as the returning object from a rejection.

Node.js has a utility called [promisify](https://nodejs.org/api/util.html#util_util_promisify_original) to ease the "promisification" of old-style callback APIs.

### Error handling in Promise.all

The static method `Promise.all` accepts an array of Promise, and returns an array of results from all resolving Promise:

```javascript
const promise1 = Promise.resolve("All good!");
const promise2 = Promise.resolve("All good here too!");

Promise.all([promise1, promise2]).then((results) => console.log(results));

// [ 'All good!', 'All good here too!' ]
```

If any of these Promise rejects, `Promise.all` rejects with the error from the first rejected Promise. 

To handle these situations in `Promise.all` we use `catch`, as we did in the previous section:

```javascript
const promise1 = Promise.resolve("All good!");
const promise2 = Promise.reject(Error("No good, sorry!"));
const promise3 = Promise.reject(Error("Bad day ..."));

Promise.all([promise1, promise2, promise3])
  .then(results => console.log(results))
  .catch(error => console.error(error.message));
```

To run a function regardless of the outcome of `Promise.all`, again, we can use `finally`:

```javascript
Promise.all([promise1, promise2, promise3])
  .then(results => console.log(results))
  .catch(error => console.error(error.message))
  .finally(() => console.log("Always runs!"));
```

### Error handling in Promise.any

We can consider `Promise.any` (Firefox > 79, Chrome > 85) as the opposite of `Promise.all`.

Whereas `Promise.all` returns a failure even if a single Promise in the array rejects, `Promise.any` gives us always the first resolved Promise (if present in the array) regardless of any rejection occurred.

In case instead **all the Promise** passed to `Promise.any` reject, the resulting error is an `AggregateError`. Consider the following example:

```javascript
const promise1 = Promise.reject(Error("No good, sorry!"));
const promise2 = Promise.reject(Error("Bad day ..."));

Promise.any([promise1, promise2])
  .then(result => console.log(result))
  .catch(error => console.error(error))
  .finally(() => console.log("Always runs!"));
```

Here we handle the error with `catch`. The output of this code is:

```text
AggregateError: No Promise in Promise.any was resolved
Always runs!
```

The `AggregateError` object has the same properties of a basic `Error`, plus an `errors` property:

```javascript
//
  .catch(error => console.error(error.errors))
//
```

This property is an array of each individual error produced by the reject:

```text
[Error: "No good, sorry!, Error: "Bad day ..."]
```

### Error handling in Promise.race

The static method `Promise.race` accepts an array of Promise:

```javascript
const promise1 = Promise.resolve("The first!");
const promise2 = Promise.resolve("The second!");

Promise.race([promise1, promise2]).then(result => console.log(result));

// The first!
```

The result is **the first Promise who wins the "race"**.

How about rejections then? If the rejecting Promise is not the first to appear in the input array, `Promise.race` resolves:

```javascript
const promise1 = Promise.resolve("The first!");
const rejection = Promise.reject(Error("Ouch!"));
const promise2 = Promise.resolve("The second!");

Promise.race([promise1, rejection, promise2]).then(result =>
  console.log(result)
);

// The first!
```

If **the rejection instead appears as the first element of the array,** `Promise.race` **rejects**, and we must catch the rejection:

```javascript
const promise1 = Promise.resolve("The first!");
const rejection = Promise.reject(Error("Ouch!"));
const promise2 = Promise.resolve("The second!");

Promise.race([rejection, promise1, promise2])
  .then(result => console.log(result))
  .catch(error => console.error(error.message));

// Ouch!
```

### Error handling in Promise.allSettled

`Promise.allSettled` is an ECMAScript 2020 addition to the language.

There is not so much to handle with this static method since **the result will always be a resolved Promise, even if one or more input Promise rejects**.

Consider the following example:

```javascript
const promise1 = Promise.resolve("Good!");
const promise2 = Promise.reject(Error("No good, sorry!"));

Promise.allSettled([promise1, promise2])
  .then(results => console.log(results))
  .catch(error => console.error(error))
  .finally(() => console.log("Always runs!"));
```

We pass to `Promise.allSettled` an array consisting of two Promise: one resolved and another rejected.

In this case `catch` will never be hit. `finally` instead runs.

The result of this code, logged in `then` is:

```text
[
  { status: 'fulfilled', value: 'Good!' },
  {
    status: 'rejected',
    reason: Error: No good, sorry!
  }
]
```

### Error handling for async/await

`async/await` in JavaScript denotes asynchronous functions, but from a reader standpoint they benefit from all the **readability** of synchronous functions.

To keep things simple we'll take our previous synchronous function `toUppercase`, and we transform it to an asynchronous function by putting `async` before the `function` keyword:

```javascript
async function toUppercase(string) {
  if (typeof string !== "string") {
    throw TypeError("Wrong type given, expected a string");
  }

  return string.toUpperCase();
}
```

Just by prefixing a function with `async` we cause the function to **return a Promise**. That means we can chain `then`, `catch`, and `finally` after the function call:

```javascript
async function toUppercase(string) {
  if (typeof string !== "string") {
    throw TypeError("Wrong type given, expected a string");
  }

  return string.toUpperCase();
}

toUppercase("abc")
  .then(result => console.log(result))
  .catch(error => console.error(error.message))
  .finally(() => console.log("Always runs!"));
```

**When we throw from an** `async` function the exception becomes cause of **rejection for the underlying Promise**.

Any error can be intercepted with `catch` from the outside.

Most important, in **addition to this style we can use** `try/catch/finally`, much as we would do with a synchronous function.

In the following example we call `toUppercase` from another function, `consumer`, which conveniently wraps the function call with `try/catch/finally`:

```javascript
async function toUppercase(string) {
  if (typeof string !== "string") {
    throw TypeError("Wrong type given, expected a string");
  }

  return string.toUpperCase();
}

async function consumer() {
  try {
    await toUppercase(98);
  } catch (error) {
    console.error(error.message);
  } finally {
    console.log("Always runs!");
  }
}

consumer(); // Returning Promise ignored
```

The output is:

```text
Wrong type given, expected a string
Always runs!
```

On the same topic: [How to Throw Errors From Async Functions in JavaScript?](https://www.valentinog.com/blog/throw-async/)

### Error handling for async generators

**Async generators** in JavaScript are **generator functions capable of yielding Promises** instead of simple values.

They combine generator functions with `async`. The result is a generator function whose iterator objects expose a Promise to the consumer.

To create an async generator we declare a generator function with the star `*`, prefixed with `async`:

```javascript
async function* asyncGenerator() {
  yield 33;
  yield 99;
  throw Error("Something went wrong!"); // Promise.reject
}
```

Being based on Promise, the same rules for error handling apply here. `throw` inside an async generator causes a Promise rejection, which we intercept with `catch`.

To **pull Promises out an async generators** we can use two approaches:
 
- `then` handlers.
- **async iteration**.

From the above example we know for sure there will be an exception after the first two `yield`. This means we can do:

```javascript
const go = asyncGenerator();

go.next().then(value => console.log(value));
go.next().then(value => console.log(value));
go.next().catch(reason => console.error(reason.message));
```

The output from this code is:

```text
{ value: 33, done: false }
{ value: 99, done: false }
Something went wrong!
```

The other approach uses **async iteration** with `for await...of`. To use async iteration we need to wrap the consumer with an `async` function.

Here's the complete example:

```javascript
async function* asyncGenerator() {
  yield 33;
  yield 99;
  throw Error("Something went wrong!"); // Promise.reject
}

async function consumer() {
  for await (const value of asyncGenerator()) {
    console.log(value);
  }
}

consumer();
```

And as with `async/await` we handle any potential exception with `try/catch`:

```javascript
async function* asyncGenerator() {
  yield 33;
  yield 99;
  throw Error("Something went wrong!"); // Promise.reject
}

async function consumer() {
  try {
    for await (const value of asyncGenerator()) {
      console.log(value);
    }
  } catch (error) {
    console.error(error.message);
  }
}

consumer();
``` 

The output of this code is:

```text
33
99
Something went wrong!
```

The iterator object returned from an asynchronous generator function has also a `throw()` method, much like its synchronous counterpart.

Calling `throw()` on the iterator object here won't throw an exception, but a Promise rejection:

```javascript
async function* asyncGenerator() {
  yield 33;
  yield 99;
  yield 11;
}

const go = asyncGenerator();

go.next().then(value => console.log(value));
go.next().then(value => console.log(value));

go.throw(Error("Let's reject!"));

go.next().then(value => console.log(value)); // value is undefined
```

To handle this situation from the outside we can do:

```javascript
go.throw(Error("Let's reject!")).catch(reason => console.error(reason.message));
```

But let's not forget that iterator objects `throw()` send the exception **inside the generator**. This means we can also apply the following pattern:

```javascript
async function* asyncGenerator() {
  try {
    yield 33;
    yield 99;
    yield 11;
  } catch (error) {
    console.error(error.message);
  }
}

const go = asyncGenerator();

go.next().then(value => console.log(value));
go.next().then(value => console.log(value));

go.throw(Error("Let's reject!"));

go.next().then(value => console.log(value)); // value is undefined
```

## Error handling in Node.js

### Synchronous error handling in Node.js

Synchronous error handling in Node.js does not differ too much from what we saw so far.

For **synchronous code**, `try/catch/finally` works fine.

However, things get interesting if we take a glance at the asynchronous world.

### Asynchronous error handling in Node.js: the callback pattern

For asynchronous code, Node.js strongly relies on two idioms:

- the callback pattern.
- event emitters.

In the **callback pattern, asynchronous Node.js APIs** accept a function which is handled through the **event loop** and executed as soon as the **call stack** is empty.

Consider the following code:

```javascript
const { readFile } = require("fs");

function readDataset(path) {
  readFile(path, { encoding: "utf8" }, function(error, data) {
    if (error) console.error(error);
    // do stuff with the data
  });
}
```

If we extract the callback from this listing, we can see how it is supposed to deal with errors:

```javascript
//
function(error, data) {
    if (error) console.error(error);
    // do stuff with the data
  }
//
```

If any errors arises from reading the given path with `fs.readFile`, we get an error object.

At this point we can:

- simply log the error object as we did.
- throw an exception.
- pass the error to another callback.

To throw an exception we can do:

```javascript
const { readFile } = require("fs");

function readDataset(path) {
  readFile(path, { encoding: "utf8" }, function(error, data) {
    if (error) throw Error(error.message);
    // do stuff with the data
  });
}
```

However, as with events and timers in the DOM, this exception will **crash the program**. The following attempt to stop it with `try/catch` won't work:

```javascript
const { readFile } = require("fs");

function readDataset(path) {
  readFile(path, { encoding: "utf8" }, function(error, data) {
    if (error) throw Error(error.message);
    // do stuff with the data
  });
}

try {
  readDataset("not-here.txt");
} catch (error) {
  console.error(error.message);
}
```

**Passing the error to another callback is the preferred option**, if we don't want to crash the program:

```javascript
const { readFile } = require("fs");

function readDataset(path) {
  readFile(path, { encoding: "utf8" }, function(error, data) {
    if (error) return errorHandler(error);
    // do stuff with the data
  });
}
```

Here `errorHandler` is what the name suggests, a simple function for error handling:

```javascript
function errorHandler(error) {
  console.error(error.message);
  // do something with the error:
  // - write to a log.
  // - send to an external logger.
}
```

### Asynchronous error handling in Node.js: event emitters

Much of what you do in Node.js is based on **events**. Most of the times you interact with an **emitter object** and some observers listening for messages.

Any event-driven module (like [net](https://nodejs.org/dist/latest-v14.x/docs/api/net.html) for example) in Node.js extends a root class named `EventEmitter`.

`EventEmitter` in Node.js has two fundamental methods: `on` and `emit`.

Consider this simple HTTP server:

```javascript
const net = require("net");

const server = net.createServer().listen(8081, "127.0.0.1");

server.on("listening", function () {
  console.log("Server listening!");
});

server.on("connection", function (socket) {
  console.log("Client connected!");
  socket.end("Hello client!");
});
```

Here we listen for two events: **listening** and **connection**.

In addition to these events, event emitters expose also an **error** event, fired in case of errors.

If you run this code listening on port 80 instead of the previous example, you'll get an exception:

```javascript
const net = require("net");

const server = net.createServer().listen(80, "127.0.0.1");

server.on("listening", function () {
  console.log("Server listening!");
});

server.on("connection", function (socket) {
  console.log("Client connected!");
  socket.end("Hello client!");
});
```

Output:

```text
events.js:291
      throw er; // Unhandled 'error' event
      ^

Error: listen EACCES: permission denied 127.0.0.1:80
Emitted 'error' event on Server instance at: ...
```

To catch it we can register an event handler for **error**:

```javascript
server.on("error", function(error) {
  console.error(error.message);
});
```

This will print:

```text
listen EACCES: permission denied 127.0.0.1:80
```

In addition, the program won't crash.

To learn more on the topic, consider also reading ["Error handling in Node.js"](https://www.joyent.com/node-js/production/design/errors).

## Wrapping up

In this guide we covered **error handling in JavaScript for the whole spectrum**, from simple synchronous code, to advanced asynchronous primitives.

There are many ways in which an exception can manifest in our JavaScript programs.

Exceptions from synchronous code are the most straightforward to catch. **Exceptions** from **asynchronous code** paths instead can be **tricky** to deal with.

In the meantime, new JavaScript APIs in the browser are almost all headed toward `Promise`. This pervasive pattern makes easier to handle exceptions with `then/catch/finally`, or with `try/catch` for `async/await`.

After reading this guide you should be **able to recognize all the different situations** that may arise in your programs, and catch your **exceptions** correctly.

Thanks for reading and stay tuned!