Reputation: 1312
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
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
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
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