I'm struggling with passing data concept in Node.js.
Let's take SQL tedious as example. Here is code from examples:
//acquire a connection
pool.acquire(function poolAcquire(err, connection) {
if (err) { console.error(err); return; }
//use the connection as normal
var request = new Request('select 1;select 2;select 3', function requestCallback (err, rowCount) {
if (err) { console.error(err); return;}
console.log('rowCount: ' + rowCount);
//release the connection back to the pool when finished
connection.release();
});
request.on('row', function onRequestRow(columns) {
console.log('value: ' + columns[0].value);
});
connection.execSql(request);
});
pool.acguire takes function as the argument and this function has particular signature (err,connection)
My question is - how do I pass SQL statement inside this function?
I cannot change signature because different function signature is not called.
Also I cannot use global scope because variable may be changed outside.
In other words I need to find way to bypass wrappers calls and still pass some data.
Something like
var mySQLs = ['select 1;select 2;select 3','select 4;select 5;'];
async.forEach(mySQLs,WorkWithOneItem, AllIsDone);
function WorkWithOneItem(item, callback){
pool.acquire(?????(item));
callback(); // tell async that the iterator has completed
}
function AllIsDone (err) {
console.log('All done');
}
By wrapping it in another function:
function aquire(sql, callback) {
pool.acquire(function poolAcquire(err, connection) {
if (err) { console.error(err); return callback(); }
//use the connection as normal
var request = new Request(sql, function requestCallback (err, rowCount) {
if (err) { console.error(err); return;}
console.log('rowCount: ' + rowCount);
//release the connection back to the pool when finished
connection.release();
callback();
});
request.on('row', function onRequestRow(columns) {
console.log('value: ' + columns[0].value);
});
connection.execSql(request);
});
}
function WorkWithOneItem(item, callback){
acquire(item, () => {
callback(); // tell async that the iterator has completed
});
}
Do you need the results out as well?
var mySQLs = ['select 1;select 2;select 3','select 4;select 5;'];
async.forEach(mySQLs, WorkWithOneItem, AllIsDone);
function WorkWithOneItem(sql, callback){
pool.acquire(function poolAcquire(err, connection) {
if (err) return callback(err);
//use the connection as normal
var request = new Request(sql, function requestCallback (err, rowCount) {
if (err) return callback(err);
console.log('rowCount: ' + rowCount);
//release the connection back to the pool when finished
connection.release();
});
var rows = [];
var count = 0;
request.on('row', function onRequestRow(columns) {
console.log('value: ' + columns[0].value);
rows.push(columns[0].value); // or whatever you actually want out of these.
});
request.on('done', function onRequestDone() {
callback(null, rows);
});
connection.execSql(request);
});
callback(); // tell async that the iterator has completed
}
function AllIsDone (err) {
console.log('All done');
// you probably want async.map, so you can get the result back
// as the second argument for a function like this
}
Related
I have this function in the controller
router.post('/', function(req, res, next) {
if (req.user.isPremium == false) {
// Free user - Single report
let website = req.body.website0;
let builtWithCall = `https://api.builtwith.com/free1/api.json?KEY=APIKEY&LOOKUP=${website}`;
let pagespeedCall = `https://www.googleapis.com/pagespeedonline/v4/runPagespeed?url=https://${website}&strategy=mobile&key=APIKEY`;
// curl 'https://www.googleapis.com/pagespeedonline/v4/runPagespeed?url=https://georgiancollege.ca&strategy=mobile&key=APIKEY'
var calls = [];
calls.push(function(callback) {
// First call
https.get(builtWithCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
// console.log('BuiltWith received', data);
});
});
});
calls.push(function(callback) {
// second call
https.get(pagespeedCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
// console.log(data);
});
});
});
} else {
// Premium user - comparison report
let websites = [];
}
async.parallel(calls, function(err, results) {
if(err){
console.log(err);
}
console.log('async callback ', results);
res.render('/generated-report', {
title: 'Report',
data: {},
});
});
});
I am trying to run several async API calls at once. The problem is, when I try to run them like this
async.parallel(calls, function(err, results) {
if(err){
console.log(err);
}
console.log('async callback ', results);
res.render('/generated-report', {
title: 'Report',
data: {},
});
});
the console doesn't log anything.
When I do the console log here though
function(callback) {
// second call
https.get(pagespeedCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
// console.log(data);
});
});
}
it logs the response. The pageSpeed one gets in a weird loop and repeats itself multiple times, but at least it works.
Now what am I doing wrong with the async.parallel part? Also what is this callback in function(callback) {?
EDIT:
This is the new version of the anonymous function:
function(callback) {
// second call
var results;
https.get(pagespeedCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
results += data;
// console.log(data);
});
resource.on('end', function(data) {
callback(null, data);
});
resource.on('error', function(err) {
callback(err);
});
});
}
You need to call the passed in callback. Looking at your one parallel function you are not calling callback(). I'll assume your resource object has an end & error
function(callback) {
// second call
var results;
https.get(pagespeedCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
results += data;
// console.log(data);
});
resource.on('end' function() {
callback(null, results);
});
resource.on('error' function(err) {
callback(err);
});
});
}
How async.parallel works is all the functions called must in turn call the passed in callback function; in your case that is callback.
Once each function in the parallel calls callback then and only then will the final function be called, which is the function you defined as function(err, results) {...}.
There is one caveat, if in the callback call you pass non-null for the first argument then that final function will be called immediately where you should handle that error if it happens.
Probably this is a promise implementation but would like to check with experts before doing so.
Need to do:
Read entire file line-by-line into MongoDB collection A.
Upon completion of step 1, Insert/Update/Delete documents from collection B based on state in collection A. If document not present in A delete from B.
Problem: Even before completion of step 1 above, step 2 starts execution and starts deleting records from B.
Tried so far: Async.series does not work. Below given is my code.
MongoClient.connect(config.mongodb.uri, function (err, db) {
if (err) {
logger.error('Unable to connect to the mongoDB server. Error:', err);
reject(err);
} else {
let startTime = new Date();
async.series([
function(callback) {
console.log('First in series');
db.collection('eligibilityStage').drop({}, function (err, oldObject) {
debugger;
var lr = new LineByLineReader(config.eligibiltyFile.fileRemoteLocation + '/' + latestEligibilityfileName);
lr.on('error', function (err) {
console.log(err);
});
var lineCount;
lr.on('line', function (line) { //** --> Jumps from here to second function in series, line#43**
if (line.length == config.eligibiltyFile.detailRecordlineWidth) {
var document = require('fixy').parse({
map: mapData, options: {
skiplines: null, fullwidth: config.eligibiltyFile.detailRecordlineWidth
}
}, line);
db.collection('eligibilityStage').insertOne(document[0], function (err, records) {
lineCount++;
if (err) {
console.log(err);
}
});
}
});
lr.on('end', function () {
console.log('File is closed, read lines:'+lineCount);
console.log('File is closed, rowcount:'+db.eigibilityStage.Count());
});
callback(null, 'loadStage');
});
},
function(callback) {
// Deletes
console.log('Series 2 function, read lines:'+lineCount);
console.log('Series 2 function, rowcount:'+db.eigibilityStage.Count());
callback(null, 'processStage');
}
],
function(err, results){
});
}
})
Am I doing it wrong? Or is this a standard problem to be solved using promise?
I am running a cron job with node with mongodb as the database. I am trying to close db connection and exit the process once the curr_1 each loop has executed completely.
However the exit() is called while function_2 is being executed. I understand this is due to the callback and is async in nature.
How do I make sure exit is called only once the curr_1.each is complete?
Any solution without promises?
function function_1(obj){
var curr_1 = coll_1.find({})
curr_1.each(function(err, doc) {
function_2(doc)
});
exit(obj)
}
function function_2(obj) {
coll_2.findOne({}, function(err, document) {
dosomeprocess(obj)
})
}
function exit(obj) {
// Close connection
console.log('closing connection')
obj.db.close();
process.exit();
}
It's a job for Node async....
For example:
async.each(
curr_1, // the collection to iterate over
function(doc, callback) { // the function, which is passed each
// document of the collection, and a
// callback to call when doc handling
// is complete (or an error occurs)
function_2(doc);
},
function(err) { // callback called when all iteratee functions
// have finished, or an error occurs
if (err) {
// handle errors...
}
exit(obj); // called when all documents have been processed
}
);
Without using any library:
function function_1(obj, callback) {
var curr_1 = coll_1.find({})
curr_1.each(function(err, doc) {
callback(err, doc);
});
}
function function_2(err, obj) {
coll_2.findOne({}, function(err, document) {
dosomeprocess(obj)
exit(err, obj);
})
}
function exit(err, obj) {
// Close connection
console.log('closing connection')
obj.db.close();
process.exit();
}
function_1(obj, function_2);
Using async module
var async = require('async');
async.waterfall([
function function_1(callback) {
var curr_1 = coll_1.find({})
curr_1.each(function(err, doc) {
if (err) {
callback(err, null)
} else {
allback(null, doc)
}
});
},
function function_2(obj, callback) {
coll_2.findOne({}, function(err, document) {
if (err) {
callback(err, null);
} else {
dosomeprocess(obj)
callback(null, obj);
}
})
}
], function done() {
obj.db.close();
process.exit();
});
Simply give a condition in your loop using counter.
function function_1(obj){
var curr_1 = coll_1.find({})
var curr_1Length = curr_1.length;
var counter = 0;
curr_1.each(function(err, doc) {
++counter;
//Check condition everytime for the last occurance of loop
if(counter == curr_1Length - 1){
exit(obj)
}
function_2(doc)
});
}
Hope it helps :)
I want to do async call from my getData function to getImage function but i am unable to get return data from getImage().Since the getData() does't wait for the completion of getImage(),as getImage() has further async db calls and therefore getData() always returns undefined.
What is the best way to do this instead doing nested callbacks?
var getData = function(id){
async.series([
function(callback){
var res = getImages(id);
callback(null, res);
}
],
// optional callback
function(err, results){
if (err) {
console.log("ERROR : " + err);
}else
{
console.log("Result: "+results);
}
});
}
var getImages = function(id){
async.series([
function(callback){
Image.find({id: id }).exec(
function(err, image) {
if (err) {
console.log(err);
callback(err, 0);
}else
{ console.log("Count: "+ image.length);
callback(null, image);
}
});
}
],
// optional callback
function(err, results){
if (err) {
console.log("ERROR : " + err);
}else
{
return results;
}
});
}
getData(1);
As you said you need to wait for getImages() to return, and you do that using promises.
Use any promise library, like q for instance:
var q = require('q')
...
var getImages = function(id){
var deferred = q.defer();
...
//do async logic that that evaluates some res obj you wish to return
db.find(..., function() {
deferred.resolve(res);
}
return deferred.promise;
}
Then, from getData(), you call it in the following matter:
getImages(id).then(
function(res) {
callback(null, res);
},
function(err) {
console.log("error:" + err);
}
);
As you are already using async - just use the waterfall functionality: https://github.com/caolan/async#waterfalltasks-callback
This way you will be able to run functions one after another and wait for the previous to finish, while still getting it's return value.
I have the following code, where i am calling a callback after my async task is completed:
var async = require("async");
function _callback(err, result){
if(err) console.log('Error Occurred');
console.log('Callback called');
console.dir(result);
}
function tasks() {
console.log('Start executing tasks');
var tasks = [];
var result = {};
tasks.push(function(_callback) {
console.log('Getting some data');
_callback(null, result);
});
tasks.push(function(_callback) {
console.log('Second function called');
_callback(null, result);
});
async.series(tasks, function(){
console.log('All done');
});
}
tasks();
I have checked against the syntax expected in the async library. Code looks similar to me.
Can someone point out whats needs to be changed here.
Try it like this:
var async = require("async");
function _callback(err, result){
if(err) console.log('Error Occurred');
console.log('Callback called');
console.dir(result);
}
function tasks() {
console.log('Start executing tasks');
var tasks = [];
var result = {};
tasks.push(function(next) {
console.log('Getting some data');
next(null, result);
});
tasks.push(function(_callback) {
console.log('Second function called');
next(null, result);
});
async.series(tasks, function(err, result){
console.log('All done');
_callback(err, result);
});
}
tasks();
Async gives every single task a callback which you need to call to let async now that this task finished.
After all tasks are finished you can call your callback.
If you want your callback to get called after every single task is finished just add before every
next(null, result) also an _callback(null, result)
The reason that wouldn't work on your implementation is that you overwrite the global _callback method with the argument that has the same name.
Hope that makes sense :)
As it stands in your code right now, the _callback function you define above should never get called, as the _callback in the tasks functions is scope to the one passed by async.
// this will never get called
function _callback(err, result){
if(err) console.log('Error Occurred');
console.log('Callback called');
console.dir(result);
}
function tasks() {
console.log('Start executing tasks');
var tasks = [];
tasks.push(function(_callback) { // the _callback here overrides the one above for this closure
console.log('Getting some data');
_callback(null, result);
});
// ... more tasks
async.series(tasks, function(){
console.log('All done');
});
}
If you want to reuse the logic in your _callback function, I'd recommend passing it the async callback as a parameter:
// rename some functions to avoid silly js scoping mysteries
function myCallback(err, result, asyncCallback){
// shared logic here
if(err) console.log('Error Occurred');
console.log('Callback called');
console.dir(result);
asyncCallback(null, result);
}
// ...
tasks.push(function(asyncCallback) { // the _callback here overrides the one above for this closure
console.log('Getting some data');
myCallback(null, result, asyncCallback);
});
// ... more tasks
async wants its callback to be called so it knows when to continue, but that doesn't mean you can't intercept it with your own handlers :P.
You don't need the function call to _callback. You could simplify it like this.
var async = require("async");
function doSomeLogic(item, callback) {
console.log(item);
callback(null, item + 2);
}
function tasks() {
console.log('Start executing tasks');
var tasks = [];
var result = {};
tasks.push(function(callback){
// do some more stuff ...
callback(null, 'one');
});
tasks.push(function(callback){
// do some more stuff ...
callback(null, 'two');
});
async.series(tasks, function(error, results) {
if (error) {
console.log(error);
}
console.log(results);
console.log('All done');
});
}
tasks();