M. Lautaro
M. Lautaro

Reputation: 97

Handle/catch errors without repeating code?

Consider the following (pseudo)function to update a database:

function my_function(req, res, input) {
  try {
    // This sanitizeInput() function throws error message if input is invalid
    sanitizeInput(input);
  catch (err) {
    res.status(500).json({id:"sql_error",message:err});
    return;
  }

  dbCreateRowPromise(input)
    .then(result => {//Handle success})
    .catch(err => {res.status(500).json({id:"sql_error",message:err}})
}

As you can see I'm writting catch twice and both times I'm writing the same response 500 status and handling the catch in the same way. Is there any good way to combine this 2 catches in a single one?

Upvotes: 0

Views: 70

Answers (4)

Gratus D.
Gratus D.

Reputation: 877

You should consider making sanitizeInput return a promise. Then you can write:

sanitizeInput(input)
.then(function(data)){
   return dbCreateRowPromise(input);
})
.then(function(data)){
   //success
})
.catch(function(error)){
   //all failures
})

I am assuming your functions return something - if not data would just be null

Upvotes: 1

Bergi
Bergi

Reputation: 665364

The easiest way is to use async/await syntax for your function:

async function my_function(req, res, input) {
  try {
    input = sanitizeInput(input);
    let result = await dbCreateRowPromise(input);
    // handle result
  } catch (err) {
    res.status(500).json({id:"sql_error",message:err});
  }
}

Alternatively, make a promise for sanitizeInput so that you can use promise chaining and handle errors from it as a rejection.

var promise;
try {
  promise = Promise.resolve(sanitizeInput(input));
} catch (err) {
  promise = Promise.reject(err);
}
promise.then(input => 
  dbCreateRowPromise(input)
).then(result => {
  // Handle success
}).catch(err => {
  res.status(500).json({id:"sql_error",message:err});
});

or

new Promise(resolve =>
  resolve(sanitizeInput(input))
).then(input =>
  dbCreateRowPromise(input)
).then(…).catch(…);

or

Promise.resolve(input).then(sanitizeInput).then(dbCreateRowPromise).then(…).catch(…)

Upvotes: 0

justin.m.chase
justin.m.chase

Reputation: 13685

It seems like what you're encountering is a mixture of synchronous code, promises and async callbacks which have different error handling techniques.

One solution is to just do what you're doing, handle the different styles of errors individually, but this could get confusing and complicated. The other is to attempt to unify all such code into a single style, using simple abstractions to smooth them over.

I would personally attempt to normalize all of my calls as async callback style calls and then unify the error handling this way. For example it looks like you're using express middleware here, so I would pass all errors out and handle them in an error handler middleware.

import { auto, constant } from 'async'
const { assign } from Object

function sanitize (opts, callback) {
  const { input } = opts
  try {
    sanitizeInput(input)
    callback()
  } catch (err) {
    callback(assign(err, { status: 'sanitize_error', statusCode: 500 })
  }
}

function createRow (opts, callback) {
  const { santizie: input } = opts
  dbCreateRowPromise(input)
    .then(result => callback(null, result))
    .catch(err => callback(assign(err, { status: 'create_row_error', statusCode: 500 }))
}

function my_function(req, res, input, next) {
  const block = {
    input: constant(input),
    sanitize: ['input', sanitize],
    createRow: ['sanitize', createRow]
  }

  auto(block, (err, result) => {
    if (err) return next(err)
    res.status(200).json({ status: 'ok' }))
  })
}

function errorHandler(err, req, res) {
  res.status(err.statusCode).json(err)
}

Upvotes: 1

Icepickle
Icepickle

Reputation: 12806

Note that in your current way of writing the code, you are not returning anything, and any following statements will assume that everything went ok, as you are handling the catch already, and transform the response.

I think you could just rewrite your statement in the following way:

function sanitizeInput( input ) {
  if (input % 2 === 0) {
    throw 'Cannot input even number';
  }
  return input;
}

function dbCreateRowPromise( input ) {
  return Promise.resolve(input);
}

function my_function( input ) {
  return Promise.resolve(input)
    .then( sanitizeInput )
    .then( dbCreateRowPromise );
}

// perfectly normall input
my_function(5)
  .then( out => console.log(out))
  .catch( err => console.log('error occured', err));
  
// errourness input
my_function(4)
  .then( out => console.log(out))
  .catch( err => console.log('error occured', err));

Just use the promises to build a chain of events, and let the error be thrown. In case it is not handled by the callee, the error will be shown in the console eventually.

Upvotes: 1

Related Questions