46

I've got a tiny module that acts as a model for my data. It sits between my routes and my database for particular data (user data in my case).

I require this module in my route code, call the subscribe method that it has, and that subscribes a user to a particular mailing list, by storing the needed data in my database. Yay!

My 'subscribe' method accepts an email and an email list ID as the two parameters. It's reasonable that I'll code sloppy and fast and put in an id for a list that doesn't exist. Spelling error, you name it.

How can I throw an error and point to the line number with that incorrect id?

Code from inside model/user.js:

if (emailLists.indexOf(listId) === -1) {
  throw new Error('listId does not exist');
}

Code from inside route.js:

user.subscribe('[email protected]', 'knewsletterr', function (error, success) {
  if (error) { return sendResponse(500, 'Ahhhhhhh!'); }
  if (!error) { return sendResponse(200, 'subscribed'); }
});

Right now, I'm getting:

/home/.../project/models/user.js:85
if (emailLists.indexOf(listId) === -1) { throw new Error('listId does not exist'); }
                                                   ^
Error: listId does not exist
3
  • listId variable is not defined in your source code Commented Oct 12, 2015 at 16:56
  • Haha, I know. I'm the one throwing the error. I just want to know how to throw the appropriate line number and filename along with the error. Commented Oct 12, 2015 at 17:02
  • 1
    var e = new Error("asdf"); console.log(e.stack) Commented Oct 12, 2015 at 17:03

2 Answers 2

53

If you're using node-style callbacks, the convention is not to throw. Instead pass you error as the first argument to your callback -

// divide with callback
function div (x, y, done) {
  if (y === 0)
    return done (Error ('Cannot divide by zero'))
  else
    return done (null, x / y)
}

div (6, 3, function (err, result) {
  // *always* check for err
  if (err)
    console.log ('error', err.message, err.stack)
  else
    console.log ('result', result)
})

Kind of a stupid function to use a callback since it can be written in a purely synchronous way, but hopefully this illustrates the pattern


Your function might already be written in a synchronous way – don't worry tho, we can convert it to a node-style callback function using something like cps2 below -

// a "normal" synchronous function that throws an error
const div = (x, y) =>
{ if (y === 0)
    throw Error ('cannot divide by zero')
  else
    return x / y
}
  
// convert it to a continuation passing style (cps) function
const cps2 = (f, x, y, k) =>
{ try
  { return k (null, f (x, y)) }
  catch (err)
  { return k (err) }
}

// logging utility for demos below
const logger = (err, result) =>
{ if (err)
    console.log ('error:', err.message, err.stack)
  else
    console.log ('result:', result)
}
  
cps2 (div, 6, 3, logger)
// result: 2

cps2 (div, 6, 0, logger)
// error: cannot divide by zero


All of that said, most peoples are using Promises nowadays. Below we demonstrate how to turn a node-style callback function into one that returns a Promise. Note, node provides this function as util.promisify, though I've implemented it here for demonstration purposes -

// a conventional function with a node-style callback
const div = (x, y, done) =>
{ if (y === 0)
    return done (Error ('cannot divide by zero'))
  else
    return done (null, x / y)
}

// convert a node-style callback function to a promise-returning function
const promisify = f => (...args) =>
  new Promise
    ( (resolve, reject) =>
        f ( ...args
          , (err, result) =>
              err
                ? reject (err)
                : resolve (result)
          )
    )

// logging utility for demos below
const logger = p =>
  p .then (console.log, console.error)
  
logger (promisify (div) (6, 3))
// 2

logger (promisify (div) (6, 0))
// Error: cannot divide by zero


Continuations are just functions tho so you can write this kind of thing in any way that you like – don't think you have to use node-style "callbacks" or Promises just because that's the only way you've seen it -

const cont = (...values) =>
  k => k (...values)

const div = (x, y) =>
  y === 0
    ? cont (Error ('cannot divide by zero'))
    : cont (null, x / y)

const logger = (err, result) =>
  err
    ? console .log ('error:', err.message)
    : console .log ('result:', result)

div (6, 3) (logger)
// result: 2

div (6, 0) (logger)
// error: cannot divide by zero

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, that's helpful! That's the first approach I took, but I do three different things depending on what happens when you subscribe someone ('already subscribed', 'success', 'some other error') so it's been tricky to separate things conceptually. Maybe I should just treat error like the app won't run errors, and handle results for everything else as a rule.
Generally speaking, throw is a synchronous method to handle errors. When using callbacks, it's assumed you're dealing with async code where throw isn't going to be particularly helpful. The idea is you don't have to catch the err in every callback in your chain. If you don't want to handle an error in the middle, you can just pass it along done(err,x); If err is null, nothing will happen, but if err is an Error, the function above has a chance to catch it.
All of that said, that doesn't mean this is the best way to design async error handling. It just happens to be the convention in node and therefore it's probably the best choice if you're writing an app/lib that others will end up using. If you're just writing the app for yourself, you can design your error handling however you feel is best for you and just use the node convention where you're interfacing with other libs.
instead of console.log ('error:', err.message, err.stack) wouldn't be also advisable to simply throw err?
@JoãoPimentelFerreira the convention is not to throw as you cannot asynchronously catch. Newer mechanisms like Promise and async/await allow for use of throw, but not in node-style callbacks.
17

This will help you !

var el = document.getElementById('el');

var log = function( val ){
  el.innerHTML+= '<div><pre>' + val + '</pre></div>';
};


try {
  
  throw Error('error in code');
  
} catch (e) {

  log( e.message );
  log( e.fileName );
  log( e.lineNumber );
  log( e.stack );

};
<div id='el'></div>

2 Comments

Thanks, I read that MDN page. It's the node.js stack trace I think I'm having issues with. I admit, I am a bit confused at this point.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.