Perform database operations in asynchronous node.js environment - node.js

I want to perform database operations in node app. What I expect is to execute queries one after one i.e. sequentially as second query is dependent on prior operation. Currently, I am using async.series module to achieve the expected results but, when I write a nested query, queries are executed asynchronously. How can I eliminate this asynchronous behavior while executing nested queries? Following is my code.
async.series([
function(callback){
dbClient.query(select_show_query,function(err,result1){
callback(err,result1.rows);
}) //dbclient query end
},//function end
function(callback){
dbClient.query(select_show_person_query,function(err,result2){
callback(err,result2.rows);
}) //dbclient query end
},//function end
function(callback){
dbClient.query(select_show_role_query,function(err,result3){
callback(err,result3.rows);
}) //dbclient query end
},//function end
function(callback){
dbClient.query(select_show_episode_query,function(err,result4){
callback(err,result4.rows);
}) //dbclient query end
},//function end
function(callback){
dbClient.query(select_show_genre_query,function(err,result5){
callback(err,result5.rows);
}) //dbclient query end
},//function end
function(callback){
dbClient.query(select_profile_photo_query,function(err,result6){
callback(err,result6.rows);
}) //dbclient query end
}//function end
],function(err,results){
if(err){
res.json({"status": "failed", "message": err.message})
}
else{
res.send(JSON.stringify(results));
}
} //function end
); //async end

use async/await of ES2017 (ES8):
async function operations(){
try {
let result1 = await dbClient.query(select_show_query);
let result2 = await dbClient.query(select_show_person_query);
let result3 = await dbClient.query(select_show_role_query);
let result4 = await dbClient.query(select_show_episode_query);
let result5 = await dbClient.query(select_show_genre_query);
let result6 = await dbClient.query(select_profile_photo_query);
} catch(err) {
return res.json({"status": "failed", "message": err})
}
// ... do something with result1.rows
// ... do something with result2.rows
// ...
}

you can user async waterfall method.
import waterfall from 'async/waterfall';
Runs the tasks array of functions in series, each passing their results to the next in the array. However, if any of the tasks pass an error to their own callback, the next function is not executed, and the main callback is immediately called with the error.
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
// Or, with named functions:
async.waterfall([
myFirstFunction,
mySecondFunction,
myLastFunction,
], function (err, result) {
// result now equals 'done'
});
function myFirstFunction(callback) {
callback(null, 'one', 'two');
}
function mySecondFunction(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
}
function myLastFunction(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
Source : https://caolan.github.io/async/docs.html

Related

Nodejs send waterfall requests

I have dynamic array with foldername and creating a zip using zipFolder
I am facing issues as the library is async i.e
zipFolder(source,dest,callback)
Because its a async call it take times and I want to block the next request unless the first one reponse is true,
function (req, res)
var folder = ['N','PP','Y'];//dynamic just an example
for( var i = 0 ; i < folder.length ; i++) {
var source = path.join(basePath,'/download/'+folder[i]);
var dest = path.join(basePath,'/createZip/'+folder[i]+'.zip');
console.log('sending',folders[i])
//this is async
zipFolder(source,dest, function(err) {
if(err) {
console.log('oh no!', err);
} else {
console.log('EXCELLENT');//want to send the second,third,fourth if success
}
});
}
Summary:
output should be like this:
sending file 1 if success/response sending file 2 if success/response
want something like waterfall wait for callback reponse to send the second file
plus I want a method to know the done call back so I can send back the reponse
you can use each as like:
var each = require('async-each-series')
var folder = ['N','PP','Y'];
each(folder, function(items,callback){
//insert each items to db in async way
var source = path.join(basePath,'/download/'+items);
var dest = path.join(basePath,'/createZip/'+items+'.zip');
zipFolder(source,dest, function(err) {
if(err) {
console.log('oh no!', err);
} else {
console.log(items,'inserted')
callback()
}
});
},function(err){
if(err) console.log('error occured')
console.log('all items uploaded');
});
Try with async waterfall method
https://caolan.github.io/async/docs.html#waterfall
Runs the tasks array of functions in series, each passing their
results to the next in the array. However, if any of the tasks pass an
error to their own callback, the next function is not executed, and
the main callback is immediately called with the error
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
UPDATE
in your usecase eachSeries is more applicable.
var folder = ['N','PP','Y'];
async.eachSeries(folder, function(item,callback){
var source = path.join(basePath,'/download/'+item);
var dest = path.join(basePath,'/createZip/'+item+'.zip');
zipFolder(source,dest, function(err) {
if(err) {
console.log('oh no!', err);
callback();
} else {
console.log('EXCELLENT');//want to send the second,third,fourth if success
callback();
}
});
},function(){
console.log('all items uploaded');
}); .

Passing a variable into asynchonous function

I found this from another question and i do understand and managed to get it to work.
But my question would be, how to pass in a variable into the first function?
var myTestSubject = "hello"; // i want to pass in this variable into the subject
async.waterfall([
function(callback){
//i want to use the variable myTestSubject here
callback(null, 'one', 'two');
},
function(arg1, arg2, callback){
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback){
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
I have tried to put it into the code like this, but it seems like it is not working. Any idea how to do it ?
var myTestSubject = "hello";
async.waterfall([
function(callback, myTestSubject ){
console.log(myTestSubject) // this is undefined
callback(null, 'one', 'two');
}], function (err, result) {
// result now equals 'done'
});
There is no need to pass myTestSubject as a parameter because its in an inner scope.
Here is an example with comments that will help you understand.
function test() {
//start of functions test scope
var myTestSubject = "hello"; // i want to pass in this variable into the subject
var two = 'not two';
console.log(two)
async.waterfall([
function(callback) {
// this is the scope of function(callback)
// but this scope is inside of function test() scope
// so everything that is above here you can use
console.log(myTestSubject)
//one is in this scope so its not available in the next function
// you will have to pass it as argument
var one = 'one';
two = 'two'
callback(null, one, 'three');
},
function(arg1, arg2, callback) {
// every function has its own scope and you can also access the outer scope
// but you cant access the sibling scope
// you will have to pass it as argument
// arg1 now equals 'one' and arg2 now equals 'three'
console.log(two)
callback(null, 'three');
},
function(two, callback) {
// in this scope you have a two variable so two is 'three'
console.log(two)
callback(null, 'done');
},
function(arg1, callback) {
// in this scope two is 'two'
console.log(two)
//but if you do
var two = 'three';
// now you have 'three' into two
callback(null, 'done');
},
function(arg1, callback) {
// in this scope you dont two is 'two'
console.log(two)
callback(null, 'done');
}
], function(err, result) {
// result now equals 'done'
});
//end of function test scope
}
First callback of waterfall does not take any args. also the order of args is incorrect in your case. following should do the work
var myTestSubject = "hello";
async.waterfall([
function(callback){
callback(null, myTestSubject)
},
function(myTestSubject, callback){
console.log(myTestSubject) // this will work
callback(null, 'done');
}], function (err, result) {
// result now equals 'done'
});

Why I can't use async.waterfall like this

I need to write unit tests for functions used in waterfall so it is necessary that they have body defined outside waterfall, but below (trial) code is not able to run, Is there anything I'm missing ?
async.waterfall([
one(cb),
two(cb)
],function(){});
var function one(cb){cb(null)}
var function one(cb){cb(null)}
Just write the functions as variables and reference them:
var one = function (cb) {console.log('1'); cb(null); }
var two = function (cb) {console.log('2'); cb(null); }
async.waterfall([
one,
two
], function() {
console.log('done');
});
Edit:
async.waterfall([
one,
two,
], function (err, result) {
// result now equals null
});
function one(cb) {
cb(null, null);
}
function two(arg1, cb) {
cb(null, arg1);
}
And try it with demo:
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
// Or, with named functions:
async.waterfall([
myFirstFunction,
mySecondFunction,
myLastFunction,
], function (err, result) {
// result now equals 'done'
});
function myFirstFunction(callback) {
callback(null, 'one', 'two');
}
function mySecondFunction(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
}
function myLastFunction(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
And read more into Link

Nodejs Async Waterfall, another flow control?

I'm using async.waterfall, and I want to do some like this:
keeping the result of async.waterfall by returning something likes Promise. So that, I can split those two parts.
function Action1(){
return async.waterfall([
function step1(callback){
...
callback(null, data);
},
function step2(data, callback){
...
callback(null, data);
},
function step3(data, callback){
...
callback(null, data);
}
]);
}
Action1().then(function(result){
// obtain the final result here
});
However, what I can find is: the final callback can only be injected into the waterfall function as an argument.
async.waterfall([
function(callback) {
...
callback(null, data);
},
function(data, callback) {
...
callback(null, data);
},
function(data, callback) {
...
callback(null, data);
}
], function (err, result) {
// final callback
...
});
Is there any suggestion for me, to achieve the expected programming structure? Or any choices other than async allow this pattern?
You should probably go for Bluebird.
The async code like this:
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
Can be written like this (using Blubird):
Promise.resolve(['one', 'two']).spread(function(arg1, arg2) {
// arg1 now equals 'one' and arg2 now equals 'two'
return 'three';
}).then(function(arg1) {
// arg1 now equals 'three'
return 'done';
}).then(function(result) {
// result now equals 'done'
});
You can refer to http://bluebirdjs.com/docs/coming-from-other-libraries.html#coming-from-async-module for more info.

Async waterfall with mongo db

Probably just a basic concept of async. I'd like to check on how we can pass the array variable from mongo result(docs) to the second function. In below code the second console.log doesn't give any output.
// Retrieve
var db = require('monk')('localhost/db');
var async = require('async');
console.log('start');
async.waterfall([
function(callback) {
var test = db.get('test');
test.find({}, function(err, docs) {
console.log(docs); //OUTPUT OK
callback(docs);
});
},
function(docs, callback) {
console.log(docs); //NO OUTPUT
}
])
To give you a better understanding of the using multiple callbacks with the async module, let's illustrate this with an example from Seven Things You Should Stop Doing with Node.js of multiple operations with callbacks to find a parent entity, then find child entities that belong to the parent:
methodA(function(a){
methodB(function(b){
methodC(function(c){
methodD(function(d){
// Final callback code
})
})
})
})
With the async module, you can either use the series method to address the use of callbacks for nesting code of multiple methods which may result in Callback Hell:
Series:
async.series([
function(callback){
// code a
callback(null, 'a')
},
function(callback){
// code b
callback(null, 'b')
},
function(callback){
// code c
callback(null, 'c')
},
function(callback){
// code d
callback(null, 'd')
}],
// optional callback
function(err, results){
// results is ['a', 'b', 'c', 'd']
// final callback code
}
)
Or the waterfall:
async.waterfall([
function(callback){
// code a
callback(null, 'a', 'b')
},
function(arg1, arg2, callback){
// arg1 is equals 'a' and arg2 is 'b'
// Code c
callback(null, 'c')
},
function(arg1, callback){
// arg1 is 'c'
// code d
callback(null, 'd');
}], function (err, result) {
// result is 'd'
}
)
Now going back to your example, using the async waterfall method you could restructure your code to
async.waterfall([
function(callback) {
// code a
var test = db.get('test');
test.find({}, function(err, docs) {
if(err) callback(err);
console.log(docs); // OUTPUT OK
callback(null, docs);
});
}], function(err, result) {
if(err) callback(err);
console.log(result); // OUTPUT OK
}
])
You must always handle your errors. Try this!
// Retrieve
var db = require('monk')('localhost/db');
var async = require('async');
console.log('start');
async.waterfall([
function(callback) {
var test = db.get('test');
test.find({}, function(err, docs) {
if(err){
console.log(err);
callback(err);
}
else if(docs){
console.log(docs);
callback(null,docs);
}
else{
console.log("No error no docs");
callback(null);
}
});
},
function(err, docs) {
if(err)
console.log(err);
else{
console.log(docs);
}
}
])

Resources