node.js - dictionary changes when passed to callback - node.js

I am using the async.map function to call a neo4j database on each element of "arrayOne".
async.map(
arrayOne,
function(item, callback){
getClientsNb({id:item},function (err, res) {
if (err) {console.log(err); return callback(err);}
console.log(res.results[0].data); //1
callback(null,res.results[0].data);
});
},
function(err,res) {
if (err) return console.log(err);
console.log(res); //2
}
)
The first console.log displays what I want:
[ { row: [ 'DIE ZAUBERFLOETE-2013', 1355 ] } ]
But the second console.log in the final function does not display the same result:
[ { row: [Object] } ],
Where does it come from? I would like to have the same result in the second console.log.
NB: the getClientsNb function is the following:
function getClientsNb(param, callback) {
request.post({
uri: httpUrlForTransaction,
json: {statements: [{
statement: 'MATCH (n:Show {id:{id}})<-[:BUYS]-(m) RETURN n.name, count(distinct m)',
parameters: param}]}
},
function (err, res, body) {
if (err) {
console.log(err);
return callback(err)
} else {
return callback(null,body)
}
}).auth('neo4j', password, true);
}

[EDITED]
By changing the second console.log() call to output the data as JSON, you can display more details about what you are actually getting back: console.log("%j", res);. The result is probably in that Object.
Additional issue
This may or may not not solve the issue you are asking about, but you do have a logic error with respect to error handling.
async.map() requires that its callback be called once for every item in the array. But in your code, if the anonymous callback passed to getClientsNb() is passed an err value, it returns without ever calling callback.
This code fixes the above issue:
async.map(
arrayOne,
function(item, callback){
getClientsNb({id:item}, function (err, res) {
if (err) { console.log(err); return callback(err); }
console.log(res.results[0].data); //1
callback(null,res.results[0].data);
});
},
function(err,res) {
if (err) return console.log(err);
console.log(res); //2
}
)

Related

The final callback function of async.each is never called. What am I doing wrong?

This function is a part of async.waterfall
The arr contains 2 objects with foldername, filename, width, height, etc.
I want to perform file operations on each of those files and put those 2 files in another array photoArr.
Then I want to pass that photoArr array to the next function in async.waterfall.
The issue is:
I am unable to reach the callback function, which is the 3rd argument to async.each. The console logs calling next but never logs oops error or here hi.
function(arr, image, callback) {
console.log("function3");
var photoArr = [];
async.each(arr, function(value, next) {
Jimp.read(`${photosDirectory}/${value["Folder"]}/${value["Photo"]}.jpg`, (err, photo) => {
if(err) next(err);
else {
photo.resize(value["Size-X(cm)"] * 37.8, value["Size Y(cm)"] * 37.8).rotate(-90);
photoArr.push(photo);
if(photoArr.length == 2) {
console.log('calling next');
next(null);
}
}
});
}, function(err) {
if(err) {
console.log('oops error');
console.log(err);
} else {
console.log("here hi");
console.log(photoArr.length);
callback(err, arr, image, photoArr);
}
});
}

functions in functions nodejs

I want to get the klout score using a screen name (twitter).
I did this. I know it doesn't work like that, but I don't know how it should work.
function get_klout(screenName){
klout.getKloutIdentity(screenName, function(error, klout_user) {
klout.getUserScore(klout_user.id, function(error, klout_response) {
return Math.round(klout_response.score);
});
});
}
I want my function to return this number Math.round(klout_response.score);
function get_klout(screenName) {
klout.getKloutIdentity(screenName, function(error, klout_user) {
klout.getUserScore(klout_user.id, function(error, klout_response) {
return Math.round(klout_response.score);
});
});
}
Your function is async, so you can't just assign what it returns to a variable because you will just assign undefined:
var result = get_klout('foo'); // undefined
what you can do is:
using async functions in node 8+
using Promises
using callbacks:
function get_klout(screenName, done) {
klout.getKloutIdentity(screenName, function(error, klout_user) {
klout.getUserScore(klout_user.id, function(error, klout_response) {
done(Math.round(klout_response.score));
});
});
}
get_klout('foo', function(response) {
console.log(response);
});
just a note:
In node is a common pattern implementing the error first callback and you should have a look on it because it's the traditional and more used way to handle errors:
http://fredkschott.com/post/2014/03/understanding-error-first-callbacks-in-node-js/
Because this is an asynchronous function you cannot use return, in order to return result from asynchronous code pass to your function callback and return the result on the callback. you should handle errors in your callbacks.
function get_klout(screenName, callback){
klout.getKloutIdentity(screenName, function(error, klout_user) {
if (err){
callback(error);
return
}
klout.getUserScore(klout_user.id, function(error, klout_response) {
if (err){
callback(error);
return
}
callback(null, klout_response.score);
});
});
}
get_klout(screenName, function(err, res){
if (err){
console.log(err);
return
}
console.log(res);
});

Asynchronous function - node.js

I have a question about asynchronous function. Here my function "My_function":
function My_function (my_name, callback){
stmt = db.prepare ("SELECT number_table1 from my_table1 WHERE user=?");
stmt.bind(my_name);
stmt.get(function(error,row){
if(error){
throw err;
}
else{
if(row){
callback(number_table1);
}
else{
console.log("error");
}
}
});
}
Work fine but I have 2 tables and I need do other query and I need add two numbers so... in my function I need do too this query:
stmt = db.prepare ("SELECT number_table2 from my_table2 WHERE user=?");
and finally return back in my callback "number_table1 + number_table2".
Somebody know how to solve it? Thanks in advance.
Best regards!
In cases like your's I like to use the async module, because the code will be more legible. Example:
var async = require('async');
function My_function(my_name, callback) {
var stmt = db.prepare("SELECT number_table1 from my_table1 WHERE user=?");
stmt.bind(my_name);
stmt.get(function (error, row) {
if (error) {
callback(error, null);
} else {
if (row) {
callback(null, number_table1);
} else {
callback(new Error("row not found"), null);
}
}
});
}
//if you need the results in a specific order, you can use "series" instead of "parallel".
async.parallel(
//array of functions
[
function (callback) {
My_function('firstName', callback);
},
function (callback) {
My_function('secondName', callback);
}
],
//results when all functions ends
//the results array will equal [number_table1, number_table2], but the order can be different, because of the parallelism
function (err, results) {
if (err) {
//handle the err
} else {
//do something
}
}
);
Docs:
http://caolan.github.io/async/docs.html#parallel or
http://caolan.github.io/async/docs.html#series
You need to synchronize the functions so that you can be sure both their results are ready before calling back. You can do this using promises: https://www.promisejs.org/
Make two regular functions (no callbacks), one for each query (function1, function2)
Make both return a promise
Then you can do
function My_function(my_name) {
var value1;
function1(my_name)
.then(function(resultFromFunction1) {
value1 = resultFromFunction1;
return function2(my_name);
})
.then(function(resultFromFunction2) {
var result = value1 + resultFromFunction2;
return result;
});
}
}
Make sure to catch errors and handle different outcomes, what I presented is its simplest form.
Update
Here is an example of a function doing a query and returning a promise
function1 = function(user) {
return new Promise(function (resolve, reject) {
pool.getConnection(function (err, connection) {
if(err) {
reject ({status : false, message : "Error in connection database"});
} else {
connection.query('SELECT number_table1 from my_table1 WHERE user=?', [user], function(err, rows){
connection.release();
if(!err) {
resolve ({status: true, message: rows});
} else {
reject ({status: false, message: err});
}
});
}
});
});
}
Make the table names function parameters. Convert that function to use async/await or promise. Use Promise.all to run both queries.

Making butch upsert in mongodb: callback never fired

I have an array of documents with unique _id and I want to insert them to my database. Some of them already in db, and for those I want to update an array property (push in array an item). All of this I need to make asyncronuosly, so after all inserted/updated I want to write response back (with callback) to client than all ok or write an error. After googling on subject I've found this solution with async module I've tried to implement it for my case. Now my code looks like this:
function processUsers(arr, listName, callback) {
var users = global.db.collection('vkusers');
var q = async.queue(function(task, cb) {
console.log('upsert butch');
users.insert(task.doc, function(err, doc) {
if (err) {
users.update({
_id : task.doc._id
}, {
$addToSet : {
mLists : listName
}
}, function(error, result){ console.log(error); console.log(result); });
}
});
}, arr.length);
for ( var doc in arr) {
q.push({
doc : arr[doc]
}, function(err) {
if (err)
callback(err, null);
})
}
q.drain = function() {
// this is the queue's callback, called when the queue is empty,
// i.e. when all your documents have been processed.
console.log('drain');
callback(null, { result: "success", upserted: arr.length });
}
}
Callback has signature callback(error, result), arr - my array of documents. I've tested it and with database everything is OK, i am getting the right result. But callback, and q.drain never fired!
You need to call async.queue's callback (cb in your code) when your insert/update is complete. Something like this:
var q = async.queue(function(task, cb) {
console.log('upsert butch');
users.insert(task.doc, function(err, doc) {
if (err) {
users.update({
_id : task.doc._id
}, {
$addToSet : {
mLists : listName
}
}, function(error, result) {
console.log(error);
console.log(result);
cb(error); // Update finished; call cb and pass in "error" so that it can bubble up if it exists
});
} else {
cb(); // Insert succeeded; call cb
}
});
}, arr.length);

How the Node.js async eachLimit works in this situation?

I wrote a little async script to batch insert a lot of JSON files into a MongoDB sharded cluster. This is my first time with this module (and I'm still learning Node.js). I don't know if I'm doing it right.
The code is the last part of a waterfall (1): previous functions end
up with an object with db, coll and files properties.
files array contains hundred of file paths and the function to
apply to each element of the array is, again, a waterfall (2).
Waterfall (2) is made of the following: read, parse, insert. When this waterfall ends (3) I call complete to finalize the processing of a single item in the array, passing the error (if any).
So far so good, correct?
What I can't understand is what happens inside the async.eachLimit callback (4). From the documentation:
A callback which is called after all the iterator functions have
finished, or an error has occurred.
That is, when all functions have finished, the next() call (5) ends the script. But the same callback (4) is invoked when a single error occurred, as per documentation. That is my script stops when a fail with a single file happens.
How can I avoid this?
async.waterfall([ // 1
// ...
function (obj, next) {
async.eachLimit(obj.files, 1000,
function (file, complete) {
async.waterfall([ // 2
function (next) {
fs.readFile(file, {}, function (err, data) {
next(err, data);
});
},
function (data, next) { // Parse (assuming all well formed)
next(null, JSON.parse(data));
},
function (doc, next) { // Insert
obj.coll.insert(doc, {w: 1}, function (err, doc) {
next(err);
});
}
], function (err, result) { // 3
complete(err);
});
},
function (err) { // 4
if (err) console.error(err);
next(null, obj); // 5
}
);
}
], function (err, obj) { // Waterfall end
if (err) console.error(err);
obj.db.close(); // Always close the connection
});
If you don't want it to break in case of an error you should just invoke the callback with a falsy first parameter, like so (look after // 3).
Is this ok with you / did I understand correctly?
async.waterfall([ // 1
// ...
function (obj, next) {
async.eachLimit(obj.files, 1000,
function (file, complete) {
async.waterfall([ // 2
function (next) {
fs.readFile(file, {}, function (err, data) {
next(err, data);
});
},
function (data, next) { // Parse (assuming all well formed)
next(null, JSON.parse(data));
},
function (doc, next) { // Insert
obj.coll.insert(doc, {w: 1}, function (err, doc) {
next(err);
});
}
], function (err, result) { // 3
if (err) {
console.log(file + ' threw an error');
console.log(err);
console.log('proceeding with execution');
}
complete();
});
},
function (err) { // 4
next(null, obj); // 5
}
);
}
], function (err, obj) { // Waterfall end
if (err) console.error(err);
obj.db.close(); // Always close the connection
});

Resources