mkopala
mkopala

Reputation: 1312

Coffeescript with callbacks & simplified error handling

I would like to be able to refactor the error handling from this coffeescript code:

# Do some stuff with 2 levels of asynchronous callbacks and error handling
vote = (res, data) ->
  Case.findOne { caseId: data.id }, (err, mycase) ->
    if err
      console.error 'Vote failed'
    else
      myvote = new Vote
        case: mycase._id
      myvote.save (err) ->
        if err
          console.error 'Could not add vote'
        else
          console.log 'Success!'

to something like this:

# Run my function, do error handling, and run the callback if no error
runit = (func, arg, errmsg, callback) ->
  func arg, (err, docs) ->
    if err
      console.log errmsg + ': ' + err
    else
      callback docs

# Original code, simplified
vote = (res, data) ->
  runit Case.findOne { caseId: data.id }, 'Vote failed', (mycase) ->        
      myvote = new Vote
        case: mycase._id
      runit myvote.save, 'Could not add vote', () ->   
          console.log 'Success!'

Obviously, the runit function needs be able to handle one or more arguments correctly, which I didn't attempt to code correctly.

If I run it like this, I get an error:

node.js:201
    throw e; // process.nextTick error, or 'error' event on first tick
          ^
TypeError: Cannot read property 'findOne' of undefined
    at /tmp/node_modules/mongoose/node_modules/hooks/hooks.js:27:28
    at /tmp/lib/api.js:227:12
    at Promise.<anonymous> (/tmp/lib/api.js:216:16)
    at Promise.<anonymous> (/tmp/node_modules/mongoose/lib/promise.js:120:8)
    at Promise.<anonymous> (events.js:67:17)
    at Promise.emit (/tmp/node_modules/mongoose/lib/promise.js:59:38)
    at Promise.complete (/tmp/node_modules/mongoose/lib/promise.js:70:20)
    at /tmp/node_modules/mongoose/lib/query.js:885:15
    at model.<anonymous> (/tmp/node_modules/mongoose/lib/document.js:181:5)
    at model.init (/tmp/node_modules/mongoose/lib/model.js:181:36)

Upvotes: 4

Views: 2409

Answers (3)

Paul Sweeney
Paul Sweeney

Reputation: 322

I'm a huge fan of Caolan's async library. The big idea with said library is that the first argument of every callback is an error, and if no error is present, the next function in the chain is called. So your could could look like this:

vote = (res, data) ->
  async.series [
    (next) -> Case.findOne { caseId: data.id }, next
    (next) -> myvote = new Vote({case: mycase_id}).save(next)
  ], (err, result) ->
    if err
      console.error err
    else
      console.log "Success!"

The chain of functions breaks on the first error thrown, that way your final callback is really only responsible for handling a single error. This is great for serial processes where you want to halt and report the first issue you run into.

Upvotes: 1

Ricardo Tomasi
Ricardo Tomasi

Reputation: 35263

Use early returns instead of conditional branches, that way you keep your code simple and sane, and avoid unnecessary boilerplate code.

vote = (res, data) ->
  Case.findOne { caseId: data.id }, (err, mycase) ->
    return console.error 'Vote failed' if err?
    myvote = new Vote
      case: mycase._id
    myvote.save (err) ->
      return console.error 'Could not add vote' if err?
      console.log 'Success!'

Upvotes: 2

thejh
thejh

Reputation: 45578

# Run my function, do error handling, and run the callback if no error
runit = (func, args..., errmsg, callback) ->
  func args..., (err, docs) ->
    if err
      return console.log errmsg + ': ' + err
    callback docs

# Original code, simplified
vote = (res, data) ->
  runit Case.findOne { caseId: data.id }, 'Vote failed', (mycase) ->        
    myvote = new Vote
      case: mycase._id
    runit myvote.save, 'Could not add vote', ->   
      console.log 'Success!'

What runit compiles to:

runit = function() {
  var args, callback, errmsg, func, _i;
  func = arguments[0], args = 4 <= arguments.length ? __slice.call(arguments, 1, _i = arguments.length - 2) : (_i = 1, []), errmsg = arguments[_i++], callback = arguments[_i++];
  return func.apply(null, __slice.call(args).concat([function(err, docs) {
    if (err) return console.log(errmsg + ': ' + err);
    return callback(docs);
  }]));
};

Upvotes: 3

Related Questions