I have a codebase which contains code similar to the code below many times:
function(doc, callback) {
doSomething(function(err) {
if(err) return callback(err);
callback(null, doc);
});
}
I'm wondering if there are any downsides to just combining the explicit error check into:
function(doc, callback) {
doSomething(function(err) {
callback(err, doc);
});
}
I understand that callback handlers are expected to check the err on callback, but in this case it's just bubbling up.
I suppose I'm wondering if based on the way callbacks are generally used, if this is an issue?
There is no difference, the code is doing the same thing. First one is just easier to edit later if you want to add some postprocessing.
Technically, second example provides a "doc" and first don't, but if somebody rely on that, they're doing it very wrong.
Related
As your project grows, we started to have this much appreciated, defensive code snippet pretty much everywhere :
func(err, result){
if(err){
console.log('An error occurred!, #myModule :' + err);
return callback(err);
}
//then the rest..
}
A quick google search reveals some libs that attempt to overcome this common concern, e.g. https://www.npmjs.com/package/callback-wrappers.
But what is the best approach to minimize the boilerplate coding without compromising the early error handling mechanism we have?
There are a couple of ways you can help to alleviate this issue, both use external modules.
Firstly, and my preferred method, is to use async, and in particular, async.series, async.parallel or async.waterfall. Each of these methods will skip straight to the last function if an error occurs in any of your async calls, thus preventing the splattering of if(err) conditions throughout your callbacks.
For example:
async.waterfall([
function(cb) {
someAsyncOperation(cb);
},
function(result, cb) {
doSomethingAsyncWithResult(result, cb);
}
], function(err, result) {
if(err) {
// Handle error - could have come from any of the above function blocks
} else {
// Do something with overall result
}
});
The other option is to use a promise library, such as q. This has a function Q.denodeify to help you wrap callback-style code into promise-style. With promises, you use .then., .catch and .done:
var qSomeAsyncOperation = Q.denodeify(someAsyncOperation);
var qDoSomethingAsyncWithResult = Q.denodeify(doSomethingAsyncWithResult);
Q()
.then(qSomeAsyncOperation)
.then(qDoSomethingAsyncWithResult)
.done(function(result) {
// Do something with overall result
}, function(err) {
// Handle error - could have come from any of the above function blocks
});
I prefer using async because it is easier to understand what is going on, and it is closer to the true callback-style that node.js has adopted.
I've just started out with NodeJS and trying to get the hang of callbacks.
Today I've seen null passed by as the first argument to the callback in many examples. Please help me understand why it's there and why I need it.
Example 1
UserSchema.methods.comparePassword = function(pwd, callback) {
bcrypt.compare(pwd, this.password, function(err, isMatch) {
if (err) return callback(err);
callback(null, isMatch);
});
};
Example 2
example.method = {
foo: function(callback){
setTimeout(function(){
callback(null, 'foo');
}, 100);
}
}
By convention in node, the first argument to a callback is usually used to indicate an error. If it's something other than null, the operation was unsuccessful for some reason -- probably something that the callee cannot recover from but that the caller can recover from. Any other arguments after the first are used as return values from the operation (success messages, retrieval, etc.)
This is purely by convention and there is nothing to stop you from writing a function that passes success as the first argument to a callback. If you plan to write a library that is adopted by other node users, you will probably want to stick with convention unless you have a very good reason not to.
I have two layers in my application(express), first is module with function which is handling database queries, fs , and so on. Second is handling requests(also known as controller/route). I just tired of all this conditions.
Sample code:
exports.updateImage = function(image, userId, callback) {
fs.readFile(image.path, function (err, imageBinary) {
if (err) callback(err);
else {
pg.connect(conString, function(err, client, done) {
done();
if (err) callback(err);
else {
client.query('UPDATE images SET data=$1, filesize=$2, filename=$3 WHERE user_id=$4', [imageBinary, image.size, image.originalFilename, userId], function(err) {
if (err) callback(err);
else callback(null);
});
}
});
}
});
};
As you can see, I callback all my errors to my controller, then it handled as internal server error. I handle database, file system possible errors, and there is too much repetitions in my code. I suppose it is bad design, and it hard to support in production. Please help me.
When you say "tired of all these conditions" I assume you're talking about all the nested callbacks and the "march off the right side of the screen" that results from that kind of directly nested callbacks? If I'm assuming incorrectly please clarify your question and I'll delete everything I'm about to write as not related. :-)
One cheap way to avoid the else structure is to instead of doing
if(err) callback(err);
else { ... stuff ... }
is to do this:
if(err) return callback(err);
Note the return: that causes execution of your function to end, nobody cares about the return values from a callback so they just get ignored. So that potentially gets rid of a layer of braces and elses.
To handle this better in general, you'll want to look at some sort of async helpers. There's three general categories of these things:
Helper libraries that manage the sequencing of multiple callbacks,
Promises, which let you represent async operations as objects, or
Language support to hide the details.
Examples of the three different types of libraries include step, flow, or async as helper libraries, for promises there's Q or when.js, and for language support look at streamline.
For more details, I did a presentation on exactly this topic about a year ago; the slides are here are there's a recording of the presentation as well.
I'm a little confused to as how to properly use the async module. Say I have:
result = long_sync_io_function();
short_sync_function(result);
... //other stuff dependent on result
Generally, in Node you convert long_sync_io_function() to its asynchronous counterpart long_async_io_function(callback(err, result)) and do something like:
long_async_io_function(function(err, result) {
if (!err) {
short_sync_function(result);
...//other stuff dependent on result
}
});
But the constant embedding of callbacks quickly means lots and lots of indentation. Is the correct way to use async:
//1
async.waterfall([
function(callback) {
result = long_sync_io_function();
callback(null, result);
},
function(result, callback) {
short_sync_function(result);
//other stuff dependent on result
}
]);
or
//2
async.waterfall([
function(callback) {
long_async_io_function(callback);
},
function(result, callback) {
short_sync_function(result);
...//other stuff dependent on result
}
]);
Are these equivalent? I can't tell if aysnc helps create asynchronous code as in 1, or just aids in structuring existing asynchronous code as in 2.
The async library has no ability to create asynchronous functions/code. Instead it is meant as helpers for higher order structure/organisation of code that's already asynchronous.
So it's number 2.
Additional answer
For simply avoiding nesting callbacks you don't need to use the async library. Simply name your functions instead of declaring them inline. So, instead of:
long_async_io_function(function(err, result) {
if (!err) {
//..do stuff dependent on result
another_async_function(function(err,result) {
//..do other stuff
});
}
});
You can write it like this:
function do_other_stuff (err, result) {
//..
}
function do_stuff_with_io_result (err, result) {
//..
another_async_function(do_other_stuff);
}
long_async_io_function(do_stuff_with_io_result );
This makes the code self documenting and much easier to debug (especially if you're stepping through in a debugger or looking at a stack trace) and you can therefore remove the redundant comments like do stuff with result and do other stuff.
Using node.js I'm creating a function to update a file that contains a JSON list by appending a new element to the list. The updated list is rewritten back to the file. If the file doesn't exist I create it.
Below, __list_append(..) does the list append and file update.
My question is if (and should) I can restructure this code to not have two calls to __list_append. I'm a bit new to node.js, and don't have a good feel for the asynchronous tactics.
function list_append(filename, doc) {
fs.exists(filename, function(exists) {
if (exists) {
fs.readFile(filename, function(err, data) {
if (err)
throw err;
__list_append(filename, JSON.parse(data), doc);
});
} else
__list_append(filename, [], doc);
});
}
It's easy to get a bit pedantic with "best practices," but when I'm writing code and I get a gut feeling that something's not right or that something could be changed, I go over some well known best practices and attempt to see if the code I'm writing adheres to them. SOLID, while being principles of object oriented programming, can be useful to think about in other contexts. In this case, it seems to me that the function is violating the Single Responsibility Principle:
One of the most foundational principles of good design is:
Gather together those things that change for the same reason, and separate those things that change for different reasons.
This principle is often known as the Single Responsibility Principle or SRP. In short, it says that a subsystem, module, class, or even a function, should not have more than one reason to change.
(This could perhaps be exchanged for Separation of Concerns or other similar principles for this example, but the concept is the same.)
In this case, the function has two responsibilities: (1) getting the current (or default) list associated with a filename, and (2) appending data to said list. A first pass at separating these concerns might look something like this:
function get_current_list(filename, callback) {
fs.exists(filename, function(exists) {
if (exists) {
fs.readFile(filename, function(err, data) {
if (err)
return callback(err);
callback(null, JSON.parse(data));
});
} else
callback(null, []);
});
}
function list_append(filename, doc) {
get_current_list(filename, function(err, list) {
if(err) throw err;
__list_append(filename, list, doc);
});
}
Now, get_current_list is only responsible for getting the current list in a file (or an empty array if there is no file), and __list_append is (assumed to be) only responsible for appending to it; list_append is now a simple integration point between these two functions. The functions are a bit more reusable and can also be tested more easily (as an aside, a test-first or TDD approach to programming can help you notice these kinds of things up front). Furthermore, repeating callback in get_current_list is quite a bit more generic than repeating __list_append; if you need to change __list_append to something else, it now is only called in one place.
This case always feels unsatisfying to me because yes, you do have to repeat your call to __list_append on both branches because only one of the branches is synchronous.
I like and up voted Brandon's answer, but this also works:
function list_append(filename, doc) {
fs.exists(filename, function(exists) {
var data = [];
if (exists) {
data = fs.readFileSync(filename, "utf8");
}
__list_append(filename, data, doc);
});
}