Capturing an error within an asynchronous callback function

I am utilizing a callback function to asynchronously set some IPs in a Redis database.

My goal is to catch any errors that occur and pass them to my error handling middleware in Express.

To purposely create an error, I have generated one within the select method, but unfortunately, the error is not being caught as expected.

Take a look at the code snippet below:

module.exports = (req, res, next) => {
  const redis = require('redis')
  const client = redis.createClient()
  try {
    client.select('2d', (err) => { // By using '2d' instead of a number, an error is intentionally triggered
      const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
      client.set(ip, true, 'EX', 120, (err, rep) => {
        return next()
      })
    })
  } catch (err) {
    err.type = 'SilentSystem'
    next(err)
  }
}

Answer №1

According to the documentation of the redis npm package, it is apparent that it follows standard Node-style callbacks. In this callback style, the first argument passed to your provided callback function is either an error or null. This is where errors are reported. Errors cannot be caught by a try/catch block because by the time the error occurs, control has already moved out of the try/catch and the surrounding function.

To handle errors in this scenario, you would do something like this:

module.exports = (req, res, next) => {
  const redis = require('redis')
  const client = redis.createClient()
  client.select('2d', (err) => { // using '2d' string instead of number deliberately generates an error
    if (err) {
      // Error handling goes here
      err.type = 'SilentSystem'
      next(err)
    } else {
      // Success handling goes here
      const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
      client.set(ip, true, 'EX', 120, (err, rep) => {
        if (err) {
          err.type = 'SilentSystem'
          next(err)
        } else {
          next()
        }
      })
    }
  })
}

In a comment, you mentioned:

My actual code is a bit more complex so I was trying to avoid repeating calling if(err) and next(err) by using try. What's a better way (less verbose) to handle errors here?

This verbosity is inherent in Node-style callbacks. One approach could be to create a filtering function to pass all results through for centralized error handling.

Alternatively, you might consider utilizing a library that "promisifies" Node-style callbacks, allowing you to use promises with their chaining mechanism for centralized error handling. A package like `promisify` can help achieve this transformation. With promisified versions of client methods, the code could be simplified as follows:

module.exports = (req, res, next) => {
  const redis = require('redis')
  const client = makeNiftyPromiseVersionOf(redis.createClient())
  client.select('2d')
    .then(data => {
      const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
      return client.set(ip, true, 'EX', 120)
    })
    .then(() => {
      next()
    })
    .catch(err => {
      err.type = 'SilentSystem'
      next(err)
    })
}

Note how the consolidated error handling at the end simplifies the process. Any errors from client operations will flow through the catch block for handling.

This setup also allows for utilizing ES2017's async/await syntax for writing asynchronous code in a synchronous style:

module.exports = (req, res, next) => {
  (async () => {
    const redis = require('redis')
    const client = makeNiftyPromiseVersionOf(redis.createClient())
    try {
      const data = await client.select('2d');
      const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
      await client.set(ip, true, 'EX', 120)
      next()
    } catch (err) {
      err.type = 'SilentSystem'
      next(err)
    }
  })();
}

As a side note, it's recommended to move the require call outside of the exported function and perform it at the module level:

const redis = require('redis')
module.exports = {
  // ...
}

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

Navigating with buttons in the Material UI Drawer

I have implemented a Material UI drawer with some modifications. The original code used buttons, but now I want to navigate to a new page when a button is clicked. For example, clicking on the 'INBOX' button should take me to a page '/new&ap ...

What is the correct way to assign a $scope variable after a successful ajax call?

Currently, I am working with Symfony and AngularJs 1.6.8 along with Symfony 3.4. Below is the configuration setup that I have: base.html.twig <html lang="en" data-ng-app="CeocApp" ng-controller="CeocController"> //css for the app <link id="ng ...

Clicking on a JQuery dropdown menu will always result in it staying open

After creating a dropdown menu click, I noticed some strange behavior. When I click the dropdown button, the menu appears as expected. However, if I move my cursor away without clicking the button again, the dropdown menu disappears and behaves like a hove ...

Ensuring jQuery ajax requests can still be made when clicking on various links

After successfully creating code for ajax requests and fetching information from the server, I encountered an issue where clicking a link would smoothly transition me to a new page without refreshing the header and footer. However, upon clicking another li ...

Pressing the Enter key does not initiate a redirect on the page

Hey there! I've set up a text field where users need to input a password in order to download a PDF file. If the password is correct, they are directed to the URL of the PDF file. However, if the password is wrong, they are redirected to a page called ...

Display the initial x list items without utilizing ngFor in Angular 2

In the template, there are 9 <li> elements, each with a *ngIf condition present. It is possible that 5 or more of them may return true, but the requirement is to only display the first 4 or less if needed. Priority is given to the order of the < ...

"Newbie in Nodejs/Express finds way to update page views without needing to reload

Currently, I am navigating Node v.0.10.29 on Windows XP SP3. My journey into the realm of Node/Express has just begun. To foster my learning, I am attempting to replicate an established PHP project. The PHP endeavor empowers users to construct a catalog ...

Is it possible to set a minimum width for browser resizing?

https://i.sstatic.net/0qhjT.png Looking at the image provided, I'm doing a comparison of the minimum resizable widths between my website and GitHub's. Is there a way to enforce a minimum width limit on my website similar to GitHub's? I&apo ...

When attempting to utilize 'express' in the main thread of Electron, I encounter an error stating the module cannot be

I am encountering an issue while using the express library in my main.js file. It functions properly during development build, but when I package the application, I receive the following error: Error: Cannot find module 'express' I am uncerta ...

Learn the art of generating multiple dynamic functions with return values and executing them concurrently

I am currently working on a project where I need to dynamically create multiple functions and run them in parallel. My starting point is an array that contains several strings, each of which will be used as input for the functions. The number of functions ...

Editable content <div>: Cursor position begins prior to the placeholder

Having an issue with a contenteditable div where the placeholder text starts behind the cursor instead of at the cursor position. Any suggestions on how to correct this? <div id="input_box" contenteditable="true" autofocus="autofocus" autocomplete="o ...

Incorrect form of SphereGeometry detected in three.js

I'm having trouble rendering a simple sphere in three.js because it's appearing distorted (looking more like a vertical stick). Below is my HTML code: <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

I am attempting to assign a default value to a TextField by retrieving data from a GetMapping call in React, however, the value is not being successfully set

I am facing an issue with setting a default value for a TextField in my code. Even though I am trying to populate it with data from a GetMapping call, the value is not being set as expected. Here is the JSON response I receive from the API call: { "id": 1 ...

Ways to eliminate the initial digit of a decimal number if it is less than 1

I need assistance with modifying float values by removing the first number if it's lower than 1 In the "OPS" table section, I am calculating the sum of OBP and SLG obtained from a database. See the code snippet below: <td>{props.player.OBP}< ...

Surprising Vercel Production Problem: Functions in Development Environment but Fails in Production Even After Reverting to a Functional Commit

In the development environment, everything runs smoothly without any issues. However, when the application is deployed to production, errors start cropping up. What's puzzling is that even after reverting to a previous commit where the production was ...

Issues have been reported with the functionality of the ng-click function in Angular JS

Having just started using AngularJs, I am facing an issue where the function is not being called when I click the button that I append. HTML </tbody> <tr> <td><input type="text" class="form-control" ng-model="contact.name ...

Contrast in functionality between a pair of variables within a controller

Could you please clarify the distinction between two variables a1 and a2: app.controller("someCtrl",function(){ this.a1=somevalue; var a2=somevalue; }); Also, can you explain the lifespan of variable a2? ...

How can I use D3.js to form a circular group in an organization structure, link it with a circular image, and connect

Is it possible to create a radial grouped circle using d3.js, similar to the image below: I have written some code as shown below. However, I am facing challenges in connecting every circle with a curved line and displaying a tooltip when hovering over a ...

Building an efficient MongoDB, Express, and React application involves properly defining the variable name that will be stored in the database

As a beginner in coding, I often struggle with the basics. Currently, I am working on creating a URL-Shortener using Express and MongoDB, along with React for the frontend. After setting up an input field and a submit button, I can successfully send the ...

Express.js - Monitoring for server closure

I have a Node.js application that utilizes Express. Within this application, there is a section of code structured as follows: const app = require('./app'); const port = process.env.PORT || 8080; const server = app.listen(port); server.on(&apos ...