Nodejs module export, wait for function to finish - node.js

I am developping an app using node js
in my export_db.js where I export the connection and query function
When I call the function, inside the export_db the output is really a string filled with information, but in my main.js the output is undefined, as if the function wasn't finished and the code continue to run before the results comes in.
How can I force it to wait ?
File export_db.js
var con = mysql.createConnection({
....
});
con.connect(function(err) {if (err) throw err;});
con.CustomQuery = function(SQLQuery){
..DO Stuff
return stringoutput="";
con.query(SQLQuery, function (err, result,fields) {
if (err) throw err;
var arr = result;
//Do Stuff Transform result into a String
stringoutput=result as string
});
return string output;
});
module.exports = con;
File import_db.js
var db = require('../db/export_db_connection');
//DO Stuff
Queryresult = db.CustomQuery(SQLQuery);
bot.reply(Queryresult) // Send the String to the bot to get a response message
//DO Stuffs

Since your code is fundamentally asynchronous in nature (you have to wait for it to be ready). It might be better to change your _db.js to export a factory function which returns a promise which resolves with an instance of the connection when it is available.
// _db.js
function connect() {
return new Promise(function (resolve, reject) {
var con = mysql.createConnection({
//....
});
con.CustomQuery = function (SQLQuery) {
return new Promise(function(resolve, reject){
con.query(SQLQuery, function(err, result, fields){
if(err) return reject(err)
// var str = convert result to string here
resolve(str);
})
})
};
con.connect(function (err) {
if (err) return reject(err);
resolve(con)
});
})
}
let conn = null;
function getConnection() {
if (!conn) {
conn = connect();
}
return conn;
}
module.exports = getConnection;
And then, when you want to use the connection:
var getConnection = require('/path/to/_db.js');
getConnection()
.then(function (conn) {
return conn.CustomQuery(sqlQuery)
})
.then(function(str){
// Query result is available here
console.log(str);
})
You can also do this without Promises using callbacks
// _db.js
function connect(cb) {
var con = mysql.createConnection({
//....
});
con.CustomQuery = function (SQLQuery) {
//..DO Stuff
// return stringoutput="";
};
con.connect(function (err) {
if (err) return cb(err);
cb(null, con)
});
})
}
let conn = null;
function getConnection(cb) {
if (!conn) {
return connect(function(err, con){
if(err) return cb(err);
conn = con;
cb(null, conn);
});
}
cb(null, conn);
}
module.exports = getConnection;
And then, when you want to use the connection:
var getConnection = require('/path/to/_db.js');
getConnection(function (err, conn) {
if(err){
// handle errors
}
QueryResult = conn.CustomQuery(SQLQuery);
})

Related

Not able to return value from promise in Nodejs

I have written the following code in Nodejs which is saving data in MongoDB:
function insertDoc(db,data){
return new Promise(resolve => {
callback=db.collection('AnalysisCollection').insertOne(data).then(function(response,obj){
console.log("Inserted record");
resolve(obj);
//console.log(obj);
// response.on('end',function(){
// resolve(obj);
// });
//return resolve(obj);
}).then(() => { return obj }
).catch(function(error){
throw new Error(error);
});
})
}
I am calling the above function from the main function like this:
async function cosmosDBConnect(nluResultJSON){
try{
//console.log("Inserting to cosmos DB");
console.log(nluResultJSON);
var url = config.cosmos_endpoint;
var result="";
var data = JSON.parse(JSON.stringify(nluResultJSON));
MongoClient.connect(url, function(err, client) {
assert.equal(null, err);
var db = client.db('NLUAnalysisDB');
// insertDoc(db, data, function() {
result=insertDoc(db, data, function() {
console.log(result);
client.close();
//return data._id;
});
});
}
catch (e) {
console.log(e);
}
}
module.exports = { cosmosDBConnect };
But in cosmosDBConnect, I am getting 'undefined' for the result, though in insertDoc I am getting the output for'obj' with _id for the inserted record.
Please help me to return this _id to cosmosDBConnect.
You are use callbacks inside of async function, which creates internal scopes. So your return aplies to them instead of whole function. You should use Promise-based methods inside of async function using await (without callbacks) or wrap whole function into own Promise otherwise.
Example:
function cosmosDBConnect(nluResultJSON) {
return new Promise((resolve, reject) => {
var url = config.cosmos_endpoint;
var result = '';
var data = JSON.parse(JSON.stringify(nluResultJSON));
MongoClient.connect(url, function(err, client) {
if (err) return reject(err);
assert.equal(null, err);
var db = client.db('NLUAnalysisDB');
insertDoc(db, data).then(obj => {
console.log(obj);
client.close();
return resolve(data._id);
});
});
});
}
Also you need to understand that your insertDoc return Promise and do not accept callback you tried to pass.
Ref: async function
result = insertDoc(db, data).then((data) => {
console.log(data);
}).catch(err => console.error(err));

NodeJS prototype function don't wait of result

Hi i have written an export module for nodejs and use to other modules in there. Unfortunately the script don't wait for the last await
authentication.prototype.adAuth = async function(loginUser, loginPassword, User) {
var authStatus = new Boolean();
var query = User.where({
username: loginUser
}).populate("provider").populate("roles");
await query.findOne(async function(err, user) {
console.log("first await");
if (err) {
authStatus = false;
}
if (user) {
if (user.provider.authModule === "activedirectory") {
var configAD = {
url: user.provider.optionFields.adURL,
baseDN: user.provider.optionFields.adUserBaseDN,
username: user.provider.optionFields.adUserDN,
password: user.provider.optionFields.adUserPassword
};
var ad = new ActiveDirectory(configAD);
await ad.authenticate(loginUser, loginPassword, function(err, auth) {
console.log("second await");
if (err) authStatus = false;
authentication.prototype.setSession(user);
console.log(auth);
authStatus = true;
});
} else {
authStatus = false;
}
} else {
authStatus = false;
}
});
return authStatus;
};
first await
POST /login 500 314.923 ms - 2497
second await
true
I don't know why the ad.authenticate doesn't wait of the result.
Presumably, because the function you are calling does not return a Promise, it uses the older style - callbacks.
You need to turn the callback function into a Promise. Try something like this:
await new Promise((resolve, reject) => {
ad.authenticate(loginUser, loginPassword, function(err, auth) {
console.log("second await");
if (err) authStatus = false;
authentication.prototype.setSession(user);
console.log(auth);
authStatus = true;
resolve();
});
})
As a general note, there's a lot of places where authStatus = false;, this suggests that you should write this piece in a more functional style. Mixing Promises and static values has a tendency to cause bugs. But that snippet above should fix your problem.

aws lambda function async connection query

I have a function connect to amazon RDS and select data from a table, but my callback function always return result is undefined. I used async/await for this function but it does't work.
My problem: I need function getOrder must be finished and return a result, after that call to callback.
My function:
'use strict';
let mysql = require('mysql');
let config = require('./config');
let pool = mysql.createPool({
connectionLimit : 10,
host : config.host,
user : config.user,
password : config.password,
database : config.database
});
exports.handler = async (event, context, callback) => {
let result = {};
try{
let sql = "SELECT * FROM tbl_test WHERE deleted = ? ";
result = await getOrder(sql,0);
}catch (err){
throw new Error(err);
}
console.log("-----Result: ",result);
return callback(null, {body: JSON.stringify(result),statusCode:200});
};
let getOrder = async (sql, params) => {
pool.getConnection((err, connection) => {
connection.query(sql, params, (err, results) => {
if (err){
throw new Error(err);
}
console.log("-----Query Done!");
connection.release();
console.log("-----Data: ", results);
return results;
});
});
};
Result when run:
result of function
To get it works, in your getOrder async function you should return a promise.
Ex:
let getOrder = async (sql, params) => {
return new Promise((resolve, reject) => {
pool.getConnection((err, connection) => {
connection.query(sql, params, (err, results) => {
if (err){
reject(err);
}
console.log("-----Query Done!");
connection.release();
console.log("-----Data: ", results);
resolve(results);
});
});
});
};

Do something async with underscore map

function addSomething(data) {
var defer = q.defer();
data = _.map(data, function(item) {
item['something'] = callSomethingAsync();
return item;
});
return defer.promise;
}
How can I handle this problem. The only way I found is using Async.js.
But maybe there is a better way using $q?
EDIT:
function getScopes(item) {
var defer = q.defer();
var query = "SELECT somevalue FROM Something WHERE ID = '" + item.id + "'";
mysql.query(query, function(err, data) {
if (err) {
defer.reject(err);
} else {
item[newkey] = data
defer.resolve(item);
}
});
defer.resolve(data)
return defer.promise;
}
//add necessary scopes to the audit
function addScopes(data) {
var promises = _.map(data, function(item) {
return getScopes(item);
});
return Promise.all(promises);
}
How I can prevent using defer in the getScopes function?
Edit 2:
var query = "SELECT * FROM tiscope";
Q.nfcall(mysql.query, query).then(function(data) {
console.log(data);
});
there is nothing returned.
Here is how I use mysql:
var sql = require('mysql');
var connection = sql.createConnection({
host : 'xxx',
user : 'xxx',
password : 'xxx',
database : 'xxx'
});
connection.connect(function(err) {
if (err) {
console.error('error connecting: ' + err.stack);
} else {
console.log('mysql connection established');
}
});
module.exports = connection;
Maybe there is the mistake.
A lot of promise libraries provide a map function. Seems Q does not. No matter the the same can be accomplished with vanilla promises (and Q) anyway using the all function.
First things first. Avoid defer. It makes code more difficult to reason and maintain. There are only a few rare cases when defer is needed. The rest of the time a normal promise constructor/helper functions will work better.
Normal Promises Example
function addSomething() {
var promises = _.map(data, function(item) {
return callSomethingAsync(item);
});
return Promise.all(promises);
}
Q Promises Example
function addSomething() {
var promises = _.map(data, function(item) {
return callSomethingAsync(item);
});
return $q.all(promises);
}
Presumably callSomethingAsync returns a promise. If not use the promise constructor pattern:
function toPromise(asyncFn, args) {
return new Promise(function (resolve, reject) {
function callback(err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
}
asyncFn(callback, args);
});
}
function addSomething() {
var promises = _.map(data, function(item) {
return toPromise(callSomethingAsync, item);
});
return Promise.all(promises);
}

MongoDB custom wrapper code check: Am I writing ok code?

I'm wondering if the code I have written here is sane and follows the rule of non-blocking, I come from a Java background so I'm new to non-blocking event loop of Node.js.
retard.js:
var MongoClient = require('mongodb').MongoClient;
var retard = {};
retard.getDb = function (url) { // url example 'mongodb://localhost:27017/myproject'
return new Promise(function (resolve, reject) {
MongoClient.connect(url, function (err, db) {
if (err)
throw err;
// custom functions
db.getCollection = function (mCollection) {
var obj = {};
var collection = db.collection(mCollection);
// access to the native drivers
obj.native = collection;
obj.findOne = function (query) {
return new Promise(function (resolve, reject) {
collection.findOne(query, function (err, result) {
if (err)
throw err;
resolve(result);
});
});
};
return obj;
};
resolve(db);
});
});
};
module.exports = retard;
This would then be used as following:
var co = require('co');
var config = require('./config');
var retard = require('./lib/retard');
co(function* () {
var db =
yield retard.getDb(config.mongodb.url);
var countries = db.getCollection('countries');
// first query
var doc =
yield countries.findOne({
country: 'scotland'
});
console.log(JSON.stringify(doc));
// second query
countries.native.findOne({
country: 'scotland'
}, function (err, result) {
if (err)
throw err;
console.log(JSON.stringify(result));
});
});
I get the results I was expecting from the database so it works. I'm just curious as to is this ok JavaScripting?
Almost there! Something to keep in mind is that that mongodb driver methods already return a Promise if no callback is passed (findone).
Rather than wrapping the methods with a new Promise, you can just return the method itself (a Promise) as follows:
return collection.findOne(query)
as opposed to:
return new Promise(function (resolve, reject) {
collection.findOne(query, function (err, result) {
if (err)
throw err;
resolve(result);
});
});
Same applies to MongoClient.connect();

Resources