I'm totally new to nodejs. I would like to ask your expertise regarding for error handling using connection.beginTransaction(); with this sample code.
connection.beginTransaction(function(){
async.parallel([
function(callback){
connection.query('INSERT INTO SUBJECT.PROJECT (Name, Score) VALUES (?,?)',
['Drake', '85']
, function(error){
//if(error)
// connection.rollback();
callback(error);
});
},
function(callback){
connection.query(someUpdateQuery, someValues,
function(error){
//if(error)
// connection.rollback();
callback(error);
});
}
], function(error){
var msg;
if(error) {
connection.rollback();
msg = 'Error! ' + error;
}
else {
connection.commit();
msg = 'Success';
}
res.json(msg);
});
});
With this sample code, Is this doable?
Every connection.query has connection.rollback() in if(error), can I remove connection.rollback() in each
connection.query and rely in the last function instead to handle the
connection.rollback()? - will it rollback all (lets say) 10 inserting query functions then the 5th 1 got the error.
Sorry, I don't know much,. thanks for reading
You are correct - you can remove the rollback statements of the individual queries and rely on the one in the async.parallel callback. Your code would look something like this:
connection.beginTransaction(function () {
async.parallel([
function (callback) {
connection.query('INSERT INTO SUBJECT.PROJECT (Name, Score) VALUES (?,?)',
['Drake', '85']
, callback);
},
function (callback) {
connection.query(someUpdateQuery, someValues, callback);
}
], function (error) {
var msg;
if (error) {
connection.rollback();
msg = 'Error! ' + error;
}
else {
connection.commit();
msg = 'Success';
}
res.json(msg);
});
});
This works because non of the commands will be committed to the database until you call connection.commit() in the final callback. One thing you might want to look at is whether the commit and rollback methods are asynchronous, i.e. they expect you to pass a callback to them. If so your code as it is will run res.json before the transaction has been committed or rolled back.
Related
I tried to define local variable then call lambda function which populates the value to my local variable:
var listOfAliases = null;
lambda.invoke(params, function(err, data) {
if (err) {
//context.fail(err);
console.log(`This is the ERROR execution =${err} =================================`);
prompt(err);
} else {
//context.succeed('Data loaded from DB: '+ data.Payload);
listOfAliases = JSON.stringify(data.Payload);
console.log(`This is the VALIDE execution =${data.Payload} =================================`); //I can see this in the log with proper values
console.log(`This is the VALIDE execution(listOfAliases) =${listOfAliases} =================================`); //I can see this in the log with proper values
}
callback(null, JSON.parse(data.Payload));
});
console.log(`This is the DB execution listOfAliases=${listOfAliases} =================================`); //I can see this in the log with NULL value
The problem here is that lambda.invoke executes asynchronously and your last console.log executes before the invoke callback function completes.
If you need to access the result from outside one the asynchronous call completes, you could use a promise.
var promise = new Promise(function(resolve,reject){
lambda.invoke(params, function(err, data) {
if (err) {
reject(err);
} else {
resolve(JSON.stringify(data.Payload));
}
});
});
promise.then(function(listOfAliases){
console.log('This is the DB execution listOfAliases ' + listOfAliases);
});
this question is related with an answer to my previous question. there #robertklep recommends me to use mapLimit() instead of .map() because .map() can't handle a large series of data, and with that solution all works fine. But now I restructured my code, and now neither of the .<fn>Limit() functions run after the first loop iteration. do I missing something here?
var proccesBook = function(file, cb) {
testFile(file, function (epub) {
if (epub) {
getEpuData(file, function (data) {
insertBookInDB(data)
})
}else{
cb(file)
}
})
}
async.mapLimit(full_files_path, 10, proccesBook, function(err){
if(err){
console.log('Corrupted file', err);
} else {
console.log('Processing complete');
};
})
// ---> only runs for the first 10 series data
Your primary issue is you don't call cb in the success branch of processBook. Your control flow must guarantee to call the callback exactly once for each worker function invocation.
Other asides:
You don't seem to need the results, so eachLimit is fine
Only need mapLimit if you need the results of each worker
You need to follow the standard error-first convention when calling the callback. Don't do cb(file) as that will be interpretted as an error and about the remaining processing.
var proccesBook = function(file, cb) {
testFile(file, function (epub) {
if (epub) {
getEpuData(file, function (data) {
insertBookInDB(data)
cb() // This is what you were missing
})
}else{
cb()
}
})
}
async.eachlimit(full_files_path, 10, proccesBook, function(err){
if(err){
console.log('Corrupted file', err);
} else {
console.log('Processing complete');
};
})
async.forEach(vsr.vehicles, function(vsr_vehicle, callback){
pjCustom.vehicleJson(vsr_vehicle, function(vehicleInitialize){
Vehicle.find({ where: { vehicleID: (vsr_vehicle.vehicleID).toString().trim() } }).success(function(vehicleFound){
if(vehicleFound){
//Code Logic is working fine.
}else{
vehicleBuild.save().success(function(vehicleNew){ // To create new vehicle of updated vsr
var vehicleBuild = Vehicle.build(vehicleInitialize)
pj.log("Update vehicle ............................")
temp.push(vehicleNew.vehicleID)
})
}
})
})
callback()
},function(){
res.send(204)
})
//vehicleJSON
exports.vehicleJson = function(vsr_vehicle, callback){
pjCustom.getVehicle(vsr_vehicle, function(status, vehicleId){
if (status == true) {
vsr_vehicle.vehicleID = vehicleId
callback(
{ 'vehicleID':vsr_vehicle.vehicleID).toString().trim(),'vsr_id':vsr_vehicle.vsr_id})
}
})
}
//getvehicle
exports.getVehicle = function(vsr_vehicle, callback){
if(vsr_vehicle.vehicleID !== undefined){
callback(true, vsr_vehicle.vehicleID)
}else{
Vehicle.find({ where: { 'vsr_id': vsr_vehicle.vsr_id },
attributes: ['id', 'vehicleID'],'order': 'id DESC', 'limit': '1'
}).success(function(vehicles){
var temp = (vehicles.vehicleID).split("-")
var newvehicleId = temp[0]+"-"+temp[1]+"-"+(parseInt(temp[2])+1)
callback(true, newvehicleId)
})
}
}
Explanation:
while inserting a record from vsr_vehicle. I need to check whether the vehicleID is present then it will fetch if not it will creates a new Id.
Consider this code is for updating a vehicle as well as inserting another "two" new vehicles. how to manage async process. of insertion of new vehicles.
it is not waiting for completion of first iteration and going for vehicleJson and generating same vehicleID for both new vehicles. suggest me to complete this challange.
My Code is clearly written here.
Please requesting before reading pls copy the code and paste in any JS editor you definitely will understand more than my explanation.
Your callback call in series.forEach is at the incorrect place. Here is the correction:
async.forEach(vsr.vehicles, function(vsr_vehicle, callback){
pjCustom.vehicleJson(vsr_vehicle, function(vehicleInitialize){
Vehicle.find({ where: { vehicleID: (vsr_vehicle.vehicleID).toString().trim() } }).success(function(vehicleFound){
if(vehicleFound){
callback(); // <--- call here
}else{
vehicleBuild.save().success(function(vehicleNew){ // To create new vehicle of updated vsr
var vehicleBuild = Vehicle.build(vehicleInitialize);
pj.log("Update vehicle ............................");
temp.push(vehicleNew.vehicleID);
callback(); // <--- call here
});
}
});
});
// callback(); // <--- Don't call here
},function(){
res.send(204);
});
BTW, for good practice, use semicolon (";") at the end of javascript statements
thanks for your help...struggling big time with how to handle this properly. I'm in async now, having given up on my ability to write the callbacks properly. I have snippet where I'm passing a set of random numbers (eachrecord) and passing them through to a mongoose call. Trying to create a data set from the multiple queries I pass.
My issue is that no matter what I've done for 4 hours, the "newarray" variable is always empty.
Thank you for your help -
async.forEach(arLimit, function(eachrecord, callback){
newarray = new Array;
var query = UGC_DB_Model.find({}).skip(eachrecord).limit(-1);
query.execFind(function (err, data) {
if (err)
console.log(err);
else {
newarray.push(data);
}
});
callback(null, newarray);
}, function(err, result) {
if (err) return next(err);
console.log("(it's empty): " + result);
});
There are several issues with your code:
async.forEach isn't meant to 'generate' results, that's what async.map is for;
you need to call the callback only when execFind is done, and not immediately after calling it;
your newarray is probably not necessary;
So try this instead:
async.map(arLimit, function(eachrecord, callback){
var query = UGC_DB_Model.find({}).skip(eachrecord).limit(-1);
query.execFind(function (err, data) {
if (err)
callback(err); // pass error along
else {
callback(null, [ data ]);
// although I think you mean this (because 'data' is probably an array already)
// callback(null, data);
}
});
}, function(err, result) {
if (err) return next(err);
console.log("(it's empty): " + result);
});
I want to use async to make node.js work something in order, first query two rates from mongodb,then use these rates to compute two new rate:
async.series([
function(callback){
db.collection('heros',function(err,collection){
if(err){
console.log(err);
}
if(!err){
console.log("2 collection fetched!");
collection.findOne({'id':win},function(err,result){
if (err) throw err;
rate1=result.rate;
console.log("win-rate:"+rate1);
});
collection.findOne({'id':lose},function(err,result){
if (err) throw err;
rate2=result.rate;
console.log("lose-rate:"+rate2);
});
}
});
callback(null);
}, function(callback){
var Ea= 1/(1+Math.pow(10,(rate2-rate1)/400));
var Eb= 1/(1+Math.pow(10,(rate1-rate2)/400));
var ra= rate1+16*(1-Ea);
var rb= rate2+16*(0-Eb);
console.log("ra:"+ra);
console.log("rb:"+rb);
callback(null);
},
function(callback){
db.collection('heros',function(err,collection){
if(!err){
collection.update({'id':win},{$set: {rate:ra}},function(err,result){
if(err) throw err;
if(!err){
console.log("update successful");
}
});
collection.update({'id':lose},{$set:{rate:rb}},function(err,result){
if(err) throw err;
if(!err){
console.log("update successful");
}
});
}
});
callback(null);
}
]);
but when I run it, it shows error messages:
ReferenceError: ra is not defined
it seems that nodejs jumps to computing or updating without waiting for the query complete.
You're declaring the variables like ra inside of a function block, so they're scoped to that function and unavailable elsewhere. You'd need to put them somewhere more accessible. For example, you could put them in a global variable:
var ra;
async.series([ ... ]);
Further, when you use series, you should call the callback function only when all of the work for that step has completed.
For example, one task should look like this:
db.collection('heros',function(err,collection){
if(err){
console.log(err); // log the error
callback(err); // call the async call back with the error
return; // stop
}
console.log("2 collection fetched!");
collection.findOne({'id':win},function(err,result){
if (err) { callback(err); return; } // again, handle error
rate1 = result.rate; // grab results
console.log("win-rate:"+rate1); // log
callback(null, rate1); // now indicate complete by calling
});
});
As you've got multiple async function calls within a single task, you may want to split them to multiple tasks or consider using the parallel function as well. You could even use parallel within one of the other tasks in the series call to handle the case where you have two findOne calls that need to be made.