In the realm of JavaScript, what happens when a function yields yet another function while also welcoming another function as an argument?

After following a Node & Express project tutorial on YouTube, I encountered the following code snippet in an async JavaScript file:

const asyncHWrapper = (fn) => {
  return async (req, res, next) => {
    try {
      await fn(req, res, next);
    } catch (error) {
      next(error);
    }
  };
};
module.exports = asyncHWrapper;

Here is how it is used:

const Task = require("../models/taskModel");
const asyncWrapper = require("../middleware/async");
const { createCustomError } = require("../errors/customErrors");
const getAllTasks = asyncWrapper(async (req, res) => {
  const tasks = await Task.find({});
  res.status(200).json({ tasks });
});

I have some questions that are confusing me:

  1. Is it necessary to return an arrow function in the asyncWrapper? Why not just call the function directly?
  2. Where do the parameters (req, res) in the asyncWrapper function come from? Are they from the "fn" function declaration?
  3. Why should two pairs of async and await be written in the wrapper and when calling it?

Thank you for any help! The tutorial link: Node.js / Express Course - Build 4 Projects

Answer №1

Before addressing your question, let me take a closer look at the code.

Here is the wrapper code snippet:

const asyncHWrapper = (fn) => {
  return async (req, res, next) => {
    try {
      await fn(req, res, next);
    } catch (error) {
      next(error);
    }
  };
};

and this is how it is implemented:

const getAllTasks = asyncWrapper(async (req, res) => {
  const tasks = await Task.find({});
  res.status(200).json({ tasks });
});

The asyncWrapper function takes an fn parameter which, in this case, is the following function:

async (req, res) => {
  const tasks = await Task.find({});
  res.status(200).json({ tasks });
}

When asyncHWrapper is invoked with the above function, it will generate another function, assigned here as getAllTasks.

Now onto your query:

  1. Is it necessary to return an arrow function within the asyncWrapper? Why not simply call the function?

In essence, you could structure it like so:

const asyncHWrapper = async (fn, req, res, next) => {
    try {
      await fn(req, res, next);
    } catch (error) {
      next(error);
    }
};

And then execute it as follows:

await asyncHWrapper(async (req, res) => {
  const tasks = await Task.find({});
  res.status(200).json({ tasks });
}, req, res, next)

But this would essentially be just a regular function with callbacks and should not be labeled as asyncHWrapper.

  1. Where do the parameters (req,res) in the asyncWrapper function originate from? Are they derived from the "fn" function declaration?

No, these parameters are sourced from getAllTasks; your fn solely consumes the values (req, res), while the next parameter serves error-handling purposes. Therefore, when calling getAllTasks, remember to pass in three arguments: getAllTasks(req, res, next)

  1. Why am I required to include two sets of async and await in the wrapper and during its invocation?

If you are referring to both utilizing await when wiring up getAllTasks and executing fn, it's because both functions are asynchronous in nature.

Answer №2

Exploring the concept of a "wrapper" can enhance your understanding. Consider a function called divide:

const divide = (a,b) => a/b;

You can easily use this in regular code:

x = divide(10,5); // sets x to 2.

If you want to account for potential errors like division by zero, you could add error handling code within the divide function itself. Alternatively, you can choose to "wrap" divide with an error handler to separate the error handling logic from the main division logic. This is where defining a safeDivide function becomes valuable:

const safeDivide = catchErrors(divide);

Just like divide, safeDivide also needs to take two arguments. Therefore, the catchErrors wrapper must return a function. Here's a starting point:

const catchErrors = (fn) => {
    return (p,q) => fn(p,q);
}

This initial setup doesn't change much. However, we can now enhance it by introducing try/catch blocks gradually:

const catchErrors = (fn) => {
    return (p,q) => {
        try {
            return fn(p,q);
        } catch (e) {
            console.log("An error occurred. Proceeding with null result.");
            return null;
        }
    }
}

The catchErrors function now returns null if an exception occurs during the execution of the passed function. Next, let’s revisit how the catchErrors wrapper function is utilized:

const safeDivide = catchErrors(divide);

When used with the divide function, the catchErrors wrapper prepares a new function that performs the division while handling any exceptions. This response addresses your initial inquiry.

Your second query pertains to the origins of 'req' and 'res'. These are argument names meant for passing into functions. They are handed over to both the wrapped function and the inner unnamed function that contains Task.find and res.status(200) calls. The Express framework will supply these arguments.

For details on the async/await elements of the wrapper, I'll address that separately.

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

Resize the shape of an image's polygon

Is there a way to independently scale a specific polygon of an image on hover? For example, I have a world map and I would like to enlarge a country when hovering over it, then return it to its original size when no longer hovering. I am familiar with us ...

Uploading files with Node.js: How multipart upload can sometimes make the file size bigger than the original

For a project involving a Nodejs Server using Express 4, I decided to write a file uploader without relying on middleware. My goal was to gain a better understanding of how Nodejs works and multipart uploads through this exercise. Here is an excerpt of th ...

Do I need a schema or model in order to retrieve data from a MongoDB database?

Is it necessary to have a model/schema in place in order to retrieve data from a MongoDB database? I currently have a Node.js application that already has data stored in MongoDB. However, the document structure may change and the schema does not allow fo ...

Avoiding unlimited re-renders when using useEffect() in React - Tips and Strategies

As a new developer, I recently built a chat application using socket io. In my code, I have the useEffect hook set to only change when the socket changes. However, I also have setMessage within the body of useEffect(), with socket as a dependency. Unfortun ...

"JQuery conflict arises due to compatibility issues between Yoast and bbPress

Following the recent update of Yoast to version 4.7, it appears that JQuery is experiencing conflicts and not running properly. I noticed that in version 2.3.5 of Yoast, JQuery runs fine and I see this code on the page: <script type='text/javascri ...

Unveil concealed information within a freshly enlarged container

As I organize my content into an FAQ format, I want users to be able to click on a link and expand the section to reveal a list of items that can also be expanded individually. My goal is to have certain list items expand automatically when the FAQ section ...

What is the best way to trigger a function to return a property value within the same React component file when using the map function?

Currently, I am dealing with a unique data structure that resembles the following: allPersons:[ { discoveriesByType:[ {type:'car',pending:0,confirmed:10, discoveries:[{data...}]}, {type:'truck',pending:1,confirmed:25 ...

Adding a Stripe pay button using Jquery append results in the return of an [object HTMLScriptElement

I'm currently working on dynamically adding the Stripe pay with card button through jQuery. I've decided to append it because I only want it to show up once a specific condition is met by the user, as well as due to the fact that the pricing can ...

Guide on how to create a cookie for visitors who are not logged in, to retrieve cart information from a database using React and Node.js

Currently, I am developing the add to cart feature for my project. For the front end, I am utilizing reactjs and for the backend, Nodejs with express. My goal is to store the cart information for users who are not logged in within the database instead of ...

Maintain loading state in parent component while the child component's react query is still loading

Within my React application, I have a parent component that sends a request to our API. While waiting for the response, a loader is displayed on the page. Once the response is received, I iterate through the data and pass each iteration to a child componen ...

What could be causing my Node.js website to have trouble locating pages in the public directory?

Embarking on my journey in web development using node.js, I encountered an issue while trying to load a particular page, which led to the following error message in my browser: Cannot GET /public/pages/terms-and-conditions.html The file structure is orga ...

The position of a jQuery element gets reset when both the show and stop animations are used

When I use jQuery's .position() to place an element, everything works fine. But as soon as I display an animation and then cancel it with an ajax call, the position of the element resets. function displayMessage(msg) { var $messageEl = $('#u ...

In a perplexing turn of events, the Dojo dtl tag logic

Dojo dtl (Django Template Language) is being used to render a widget by passing an array with multiple objects. The iteration over the objects and their subarrays is functioning properly, but there seems to be an issue with applying an 'if' condi ...

An interactive 3D model emerges against a sleek ebony backdrop on my online platform

I stumbled upon a three.js 3D object featuring a unique touch - a 404 text with a floating orb replacing the zero. Upon importing its code, it rendered successfully, albeit against a black background. Despite my efforts to tweak values and apply background ...

Having issues with "return false" not functioning properly in jQuery ajax calls

I have been developing a registration form using jQuery ajax. Below is the code snippet I am working with: function validateData() { var email = jQuery("#email").val(); var username = jQuery("#username").val(); var emailReg = /^([\w-&bsol ...

Despite awaiting them, promises are not resolving synchronously

I have a function that retrieves location information and returns a promise. I use mobx to manage the store, updating the this.locationStoreProp and this.hotel.subtext properties. public fetchPropertyLocation(some_input_params): Promise<any> { ...

Can you share the method for concatenating an object at a specific index within an Array using JavaScript?

Currently, I have an array arr = [object1, object2, object3]. After removing the second index, the array now looks like arr = [object1, object3], and I am looking to add back the removed object2 at its original position in the array so that it becomes a ...

Click a button to spin images around

Is there a way to rotate an image by 90 degrees, 180 degrees, and 360 degrees with the click of a button? <img class="test" id="image" src="images/image" alt="This is the Display Image" /> I attempted to use the following script, but was not satisf ...

Enhance your AngularJS table with a custom Javascript context menu!

In the process of developing a shift planner, I have implemented a JS context menu and am attempting to display the shifts using Angular. However, upon clicking any table cell, all cells are being updated simultaneously. Is there a workaround for this issu ...

How can you use jQuery to activate a specific tab?

I have 3 tabs with 3 different ids. $(document).ready(function() { $(function() { $( "#tabs" ).tabs(); }); $("#links > a").click(function() { var link = $(this).attr('href').substr(-1,1); console.log(link); $('#t ...