I've read that is bad practice to mix synchronous and asynchronous operations in a function (e.g. http://nodejs.org/api/process.html#process_process_nexttick_callback).
Now i want to iterate a array of documents which i want to save in the database. Before the save begins, i need to validate all documents by step. A specific validator can be created later outside from the api.
The validate function can be completly synchronous (simple checking fields if they are valid like doc.sum > 10) but also asynchronous (if i need to get and validate a dependency document).
With the async library (https://github.com/caolan/async) i have something like this:
function validateAll(docs) {
async.eachSeries(docs, function (doc, callback) {
var async = false;
validate(doc, function () {
if (async) {
callback();
} else {
process.nextTick(function () {
callback();
});
}
});
async = true;
});
}
function validate(doc, callback) {
if (doc.sum < 10) {
return callback(new Error("Invalid sum"));
}
if (doc.otherDocumentId) {
validateOtherDocFromDbAsync(doc.otherDocumentId, callback); // collection.find() or something
} else {
callback();
}
}
In this example, i set the iteration callback on the next tick to avoid a "Maximum call stack size exceeded" error if many callbacks are called synchronously.
I have also thought to create validateSync and validateAsync functions to exactly define what they do.
Is this a bad practice? How would you design the api with this example?
Is this a bad practice?
Yes. http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony
How would you design the api with this example?
Instead of passing a callback to validate
that handles whether it was called asynchronously or not, you should just make the validate
function always asynchronous:
function validateAll(docs) {
async.eachSeries(docs, validate);
}
function validate(doc, callback) {
if (doc.sum < 10) {
process.nextTick(function () {
callback(new Error("Invalid sum"));
});
} else if (doc.otherDocumentId) {
validateOtherDocFromDbAsync(doc.otherDocumentId, callback); // collection.find() or something
} else {
process.nextTick(callback);
}
}