Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

We (and the JS SO chat room) had a talk with @rlemon some days ago about his Little-XHR library about error handling.

Basically, we wanted to decide which error handling pattern should be used:

xhr.get({
    // Some parameters, and then
    success: function(data) {},
    failure: function(data) {}
})

Or:

xhr.get({
    // Some parameters, and then
    callback: function(err, data) {}
})

One is more jQuery-like, while the other is more Node-like. Some say that the first pattern makes you think more about handling error. I think the opposite, since you may forget the other callback function, while the argument is always there on the second pattern.

Any opinion/advantage/drawback about both these patterns?

share|improve this question
    
xhr.get({ ... }, function (err, data) {}) At least get the pattern right –  Raynos Apr 12 '12 at 14:40
add comment

3 Answers

Generally, I like to remember that explicit is always better than implicit.

Using this, I'd typically side with explicit success and failure functions - you know exactly what you're dealing with the moment you open that code up - success deals calls that finished successfully, while error deals with calls that had a problem.

The alternative, using a single method, will take longer to read when you go to modify that code. Besides, you'd likely end up with something like this;

xhr.get({
    callback: function(err, data) {
        if (err) {
            // handle that error somehow
        }
        else {
            // deal with success somehow
        }
    }
})

And that kind of boilerplate gets boring, fast.

Not to mention, if you forget to add this boilerplate, and for example, you're only handling success, then a new developer entering the code base may not see see a problem with it. But having explicit error/success callbacks, they'd be able to see pretty quickly that you're missing an error callback and start working on a way to handle it, or at least figure out "well, this is only handling success - I must find a way to handle errors". It makes the code seem less magical.

share|improve this answer
    
its harder to see your missing an error callback and easier to see your not handling the first err parameter –  Raynos Apr 12 '12 at 14:36
add comment

Separate callbacks

If the xhr.get() call succeeds, then err is redundant. If the call fails. data is redundant. Rather than forcing client code to check the state of one or the other, don't pass both.

If it turns out that success could represent partial success, indicate that separately. Failure is usually the bail-out option.

I've worked with developers that only ever handle the success case and in many scenarios just implementing a success callback would suffice in this case. A multi-state callback would be catastrophic option for that style of programming as they'd assume success.

share|improve this answer
add comment

The real important feature is style consistency so you can write code in the same style and you can make meta programming assumptions about how asynchronous situations are handled.

I personally prefer

(err, data) because it's a standard way of handling things. It allows for function composition.

For example after.map uses this pattern. So code like

after.map(["foo.js", "bar.js"], function (fileName, callback) {
    fs.readFile(fileName, function (err, file) {
        callback(err, file)
    })
}, function (err, files) {
    // handle files
})

can be simplified to

after.map(["foo.js", "bar.js", fs.readFile, function (err, files) {
    // handle files
})

Another advantage is that you can pass in a callback as a last parameter

asyncOperation(options, function (err, data) {
    // not nested inside an object literal
})

The callback last approach is a nice API familiarity approach.

A further advantage is that you can easily forget to set the error handler in your object literal, or set it to some kind of default error handler.

When your using (err, data) it reminds you to think about how to efficiently handle this err every time.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.