My question is because I believe that maybe there is a better way to design my code when I need to do sync tasks in JS and wait for the response.
I'm working on Node and I need to save some data (I have a function that do it), but I need to perform some checks before (vs the database) that are async calls also.
I will simplify code and examples:
function save(req, res) {
var id = req.param('id');
var name = req.param('name');
if (id) {
// Here come the checks, e.g., there is no limit in number of items on DB
mymodule.getConfig("maxItems", function(data) {
if (data.success) {
// max = 0 means unlimited items
if (data.maxItems == 0) {
// Just continue, all fine
} else {
mymodule.countItems(function(data) {
if (data.success) {
if (data.count >= maxItems) {
res.render('myPage', {error: 'You have reached the maximun items'});
}
} else {
res.render('myPage', {error: 'Error in DB'});
}
});
}
} else {
res.render('myPage', {error: 'Error in DB'});
}
});
// Save part (common code)
mymodule.save({
'id': id,
'name': name
}, function(data) {
if (data.success) {
res.render('myPage');
} else {
res.render('myPage', {error: 'Error while saving in DB'});
}
});
} else {
res.render('myPage', {error: 'Id is null'});
}
}
Obviously this will not work because checks will be called async and save will be called always, instead of the redirection when the checks fail.
The right way is to "duplicate" the save part in the right place, like this:
function save(req, res) {
var id = req.param('id');
var name = req.param('name');
if (id) {
// Here come the checks, e.g., there is no limit in number of items on DB
mymodule.getConfig("maxItems", function(data) {
if (data.success) {
// max = 0 means unlimited items
if (data.maxItems == 0) {
// Save part---------------------------
mymodule.save({
'id': id,
'name': name
}, function(data) {
if (data.success) {
res.render('myPage');
} else {
res.render('myPage', {error: 'Error while saving in DB'});
}
});
} else {
mymodule.countItems(function(data) {
if (data.success) {
if (data.count >= maxItems) {
res.render('myPage', {error: 'You have reached the maximun items'});
} else {
// Save part-------------------------
mymodule.save({
'id': id
'name': name
}, function(data) {
if (data.success) {
res.render('myPage');
} else {
res.render('myPage', {error: 'Error while saving in DB'});
}
});
}
} else {
res.render('myPage', {error: 'Error in DB'});
}
});
}
} else {
res.render('myPage', {error: 'Error in DB'});
}
});
} else {
res.render('myPage', {error: 'Id is null'});
}
}
This works but is a wrong way to code, duplicating the same code. To respect the DRY principle, currently I'm doing a function with a callback with the save part, and calling that function in each place, like this:
function saveData(id, name, function(onComplete) {
mymodule.save({
'id': id,
'name': name
}, function(data) {
if (data.success) {
onComplete('');
} else {
onComplete('Error while saving in DB');
}
});
});
function save(req, res) {
var id = req.param('id');
var name = req.param('name');
if (id) {
// Here come the checks, e.g., there is no limit in number of items on DB
mymodule.getConfig("maxItems", function(data) {
if (data.success) {
// max = 0 means unlimited items
if (data.maxItems == 0) {
// Save part---------------------
saveData(id, name, function(cb) {
res.render('myPage', {error: cb});
});
} else {
mymodule.countItems(function(data) {
if (data.success) {
if (data.count >= maxItems) {
res.render('myPage', {error: 'You have reached the maximun items'});
} else {
// Save part---------------------
saveData(id, name, function(cb) {
res.render('myPage', {error: cb});
});
}
} else {
res.render('myPage', {error: 'Error in DB'});
}
});
}
} else {
res.render('myPage', {error: 'Error in DB'});
}
});
} else {
res.render('myPage', {error: 'Id is null'});
}
}
I'm searching for a better way to do this because I'm still calling two (or more times if needed) the saveData
function. The ideal flow was the first one, but due to the javascript async nature, I cannot make a "sequential" flow with "returns" with res
calls for checkings, and a unique save
call at the end if all worked fine.
Is there another simpler way to accomplish this?