Handling Errors in Asynchronous Functions with JavaScriptLet's explore the best practices for

I am a beginner in javascript and recently delved into async/await. After going through various resources, I gained a basic understanding. However, while experimenting with some code examples, I encountered unexpected results which left me puzzled about where I went wrong.

Code:

var colors = ["RED", "GREEN", "YELLOW"];

const getColor = async () => {
  var value = "";
  colors.forEach((color) => {
    value = value + color + " ";
  });
  return value;
};


const middleware = async () => {
  addColor(null)
    .then(() => {
      getColor().then((result) => {
        console.log(result);
      });
    })
    .catch((err) => {
      console.log(err.message + " at middleware");
    });
};

const addColor = async (color) => {
  validateColor(color)
    .then(() => {
      console.log("Adding data");
      colors.push(color);
    })
    .catch((err) => {
      console.log(err.message + " at add color");
      throw err;
    });
};

const validateColor = async (color) => {
  if (color == null) {
    throw new Error("Color cannot be empty");
  }
};

middleware();



Upon executing the middleware function, instead of only displaying the error message as expected, the output included the names of colors as well. Output:

I am perplexed as to why the code inside then() is being executed even when addColor() throws an error. Furthermore, it's puzzling why the catch block within middleware() is not being triggered.

Answer №1

When it comes to validateColor() and addColor(), they both generate Promise objects but are independent of each other. If you tweak addColor() to instead return the Promise from validateColor(), your code will function as intended:

    var colors = ["RED", "GREEN", "YELLOW"];
    
    const getColor = async () => {
      var value = "";
      colors.forEach((color) => {
        value = value + color + " ";
      });
      return value;
    };
    
    
    const middleware = async () => {
      addColor(null)
        .then(() => {
          getColor().then((result) => {
            console.log(result);
          });
        })
        .catch((err) => {
          console.log(err.message + " at  middleware");
        });
    };
    
    const addColor = async (color) => {
      return validateColor(color)
        .then(() => {
          console.log("Adding data");
          colors.push(color);
        })
        .catch((err) => {
          console.log(err.message + " at add color");
          throw err;
        });
    };
    
    const validateColor = async (color) => {
      if (color == null) {
        throw new Error("Color cannot be empty");
      }
    };
    
    middleware();

Your async functions wrap the return values in a Promise. Consequently, both validateColor() and addColor() will unknowingly create distinct Promises unless addressed. In contrast, by having addColor send back the same Promise that validateColor() generates, there is no additional separate Promise formed; the original Promise is passed back to middleware(). As this Promise contains the pending error thrown in validateColor(), the .catch() block will execute.

Answer №2

If I had to sum it up in one sentence, I would say that every async function needs to include a return promise statement - here is the corrected code.

var colors = ["RED", "GREEN", "YELLOW"];

const getColor = async () => {
  var value = "";
  colors.forEach((color) => {
    value = value + color + " ";
  });
  return Promise.resolve(value);
};


const middleware = async () => {
  return addColor(null) 
    .then(() => {
      getColor().then((result) => {
        console.log(result);
      });
    })
    .catch((err) => {
      console.log(err.message + " at middleware");
    });
};

const addColor = async (color) => {
  return validateColor(color)
    .then(() => {
      console.log("Adding data");
      colors.push(color);
    })
    .catch((err) => {
      console.log(err.message + " at add color");
      throw err;
    });
};

const validateColor = async (color) => {
  if (color == null) {
    throw new Error("Color cannot be empty");
  }
  Promise.resolve(true);
};

middleware();

Explanation:

Simply put, an async function must return a promise in order for await statements to work effectively.

Example:

For instance, when calling a function as “test()” versus “await test()”, the former will execute and proceed with subsequent statements even during blocking operations, while the latter will wait for those operations to complete before moving on.

In this scenario, none of the async functions return promises by default, causing issues upon completion of their execution.

Detailed explanation specific to this example:

The initial call to “middleware()” triggers the chain of functions starting with “addColor()”.

addColor():
If the promise resolved from this function, it proceeds to call “getColor()”. Otherwise, it displays an error message.

Within addColor, we invoke validateColor() which performs the following check:

validateColor():
Upon resolving its promise, the color is added to the array; otherwise, an error is thrown.

Since validateColor encounters an error due to a null color, addColor erroneously assumes successful completion of its task.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using React.render to render an empty subcomponent within a jsdom environment

Experimenting with React in node using jsdom has been an interesting challenge for me. I encountered an issue when attempting to render a component that includes another component with content, only to have the subcomponent's content fail to render. F ...

Understanding the Typescript Type for a JSON Schema Object

When working with JSON-schema objects in typescript, is there a specific type that should be associated with them? I currently have a method within my class that validates whether its members adhere to the dynamic json schema schema. This is how I am doing ...

Prevent multiple instances of Home screen app on iOS with PWA

There seems to be an issue with the PWA app not functioning properly on iOS devices. Unlike Android, where adding an app to your homescreen will prompt a message saying it's already installed, iOS allows users to add the app multiple times which is no ...

Enhancing User Experience: Creating a Vue Button Component for Adding Items to Cart with the Power of Axios and Laravel Backend Integration

I have recently developed a Vue3 shopping cart with an "Add One" button feature. When the user clicks this button, it updates an input field of type "number" and sends a request to update the database using Axios along with Laravel's createOrUpdate me ...

Trigger a callback in KnockoutJS once all bindings have been successfully set up

I am facing a frustrating bug that is detailed here: <select> only shows first char of selected option and I am looking for a solution to remove the display:none from my select boxes in order to resolve this issue. Any ideas? Here's the binding ...

Extracting <p> elements from a separate HTML file and cycling through them

I successfully created a website using only HTML, JavaScript, and jQuery without any server-side scripting or language. Within my website, I have a simple stream.html page that remains unstyled with no CSS formatting. <html> <head> </head& ...

Execute the function within setInterval only one time

I have a setInterval function that calculates the time difference between a specified date and the current time. Once this difference is less than an hour, I want to execute some code only once. const countdownDate = new Date('March 15, 2021 11:30:00& ...

I'm having trouble with using setInterval() or the increment operator (i+=) while drawing on a canvas in Javascript. Can anyone help me out? I'm new

I am looking to create an animation where a square's stroke is drawn starting from the clicked position on a canvas. Currently, I am facing an issue where the values of variables p & q are not updating as expected when drawing the square at the click ...

A step-by-step guide to implementing the PUT function in AngularJS using Mongoose

My attempt to send a GET request to the Mongo works fine, but I'm having trouble getting the PUT request to work. My suspicion is that there might be an issue with the path from the controller to the router. I've already tried using both update() ...

What is the best way to dynamically populate a table with JSON data that is received from an API response?

Currently, I have a table that contains the FORM element: <table id="example" class="sortable"> <caption><h3><strong>Product inventory list</strong></h3></caption> <thead> <tr ...

Finding alternative solutions without using the find() method when working with Angular

How can I use an equivalent to find(".class") in AngularJS? I came across this solution: //find('.classname'), assumes you already have the starting elem to search from angular.element(elem.querySelector('.classname')) I attempted to ...

The Vue.js Vuetify.js error message is saying "A mystery custom element: <v-list-item>, <v-list-item-title> - Have you properly registered the component?"

I followed the instructions from Vuetify Data Iterator Filter section I am able to use various Vuetify components like v-btn, v-card, v-data-table, v-data-iterator, and more. However, I encountered errors only with <v-list-item> and <v-list-item ...

Implement an event listener on the reference obtained from the React context

Within the React context provider, a ref is established to be utilized by another component for setting a blur event listener. The issue arises when the blur event fails to trigger the listener. The following is a snippet of code from the context provider ...

Execute asynchronous JavaScript request

When a user types something into the input id=2, an ajax function triggers. Here is the HTML: <input id="2" type="text" onkeyup="posttitulo(this.value)" /> And here is the SCRIPT: function posttitulo(value){ $.post("getdata/posttitulo.php",{p ...

Message displaying successful AJAX response

I'm currently updating my AJAX request to make it asynchronous, but I am wondering if there is an equivalent to var response = $.ajax({ in the success function. Previously, my code looked like this: var response = $.ajax({ type : "GET ...

Issue with OnChange Not Triggering

Why isn't the onchange property firing when typing in my textbox? Check out the code below: VB.NET Dim textBoxUrlYoutube As New TextBox divUrlTextBoxContainer.Controls.Add(textBoxUrlYoutube) textBoxUrlYoutube.CssClass = "textboxyo ...

Vue component failing to display data passed as props

As a Vue beginner, I ventured into creating a custom component and attempted to bind everything just like in the basic Vue CLI template. Here is my code snippet. Circle.vue <template> <div :style="custom"> </div> </template&g ...

What is the process for including a JavaScript file in an HTML document?

Greetings to all and thank you for taking the time! I have a straightforward query for you.. I have designed an html page featuring only a basemap (open street map). Moreover, I possess a javascript file which utilizes your coordinates to calculate a perce ...

Combining Javascript and Django for a powerful web development solution

Having trouble setting up JS on my Django web app, despite reading through the documentation and previous queries. Using the Django dev server with the following file structure: mysite/ __init__.py MySiteDB manage.py settings.py ...

Populate several input boxes with data derived from a single input field

I am facing an issue with three textboxes in my project. When I type something in the first textbox, the value is sent to state.jsp and displayed using out.println(firsttextboxvalue); on the response ID of the second textbox. However, I want to populate th ...