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();
Related
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));
i am not able to access variable outside the SearchData.forEach loop here is my code
SearchDataJson =[];
var searchdataArray = [];
SearchData.forEach( async function(item){
var sql = "SELECT email , mobile from user_profile where id in (228,192) limit 2";
var tempData = function() {
return new Promise(function (resolve, reject) {
pool.query(sql, function (err, results, fields) {
if (err) return reject(err);
return resolve(results);
});
});
};
await tempData().then( async function(results) {
SearchDataJson.push({KeyTags:item.hashtags,Data:results});
});
console.log(SearchDataJson);
});
I am new to JS. here is my function, so when I use this, it will return an empty list.
if I replace resultArray.push(result); with console.log(result); it will correctly show me the result.
I am expecting to see the function return the query result inside a list.
function queryMongo (key) {
var url = "mongodb://user:pw/task?replicaSet=bdb";
var resultArray = [];
// connect to the mongoDB
MongoClient.connect(url, { useUnifiedTopology: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("task");
var query = { 'device' : key };
var cursor = dbo.collection('commands').find(query);
cursor.forEach(function(result){
if (err) throw err;
resultArray.push(result);
}, function(){
db.close();
})
});
return resultArray;
}
As shown in here one way would be to get the whole dataset at once:
async function queryMongo(key) {
var url = 'mongodb://user:pw/task?replicaSet=bdb'
// connect to the mongoDB
return new Promise(function (resolve, reject) {
MongoClient.connect(url, { useUnifiedTopology: true }, function (err, db) {
if (err) throw err
var dbo = db.db('task')
var query = { device: key }
// Fetch all results
dbo
.collection('commands')
.find(query)
.toArray(function (err, items) {
if (err) {
return reject(err)
}
resolve(items)
db.close()
})
})
})
}
async function doWork() {
// without "async" keyword it's not possible to "await" the result
console.log(await queryMongo('some-key'))
}
doWork();
Please take following advices into consideration:
Don't connect/disconnect at each function call aside if you're performing queryMongo() once in a while
The reason your code was not working is because you were returning the result before the async call was actually finished. Hence when doing queryMongo() result was straightly []. Indeed, establishing the connection to mongo and performing the query takes "time" and NodeJS will continue performing code execution while this is happening. I would advise to read a bit around the event loop to understand this mechanic a bit better.
I think that the main problem is that all db calls are asynchronous. You can't just return the value, you can pass to a callback or return Promise.
const MongoClient = require('mongodb').MongoClient;
function queryMongo(key, callback) {
const url = 'mongodb://localhost:27017';
MongoClient.connect(url, { useUnifiedTopology: true }, function(err, db) {
if (err) throw err;
const dbo = db.db('tasks');
const query = { device: key };
dbo
.collection('commands')
.find(query)
.toArray((err, doc) => {
if (err) throw err;
db.close();
callback(doc);
});
});
}
queryMongo(12, doc => {
/* do something */
console.log(doc);
});
I have a function named looper2(), which is accepting an array of data, and im trying to get the id of each data from mysql database.All is working fine, but my nodejs is not waiting for the function, when its still represent await.
var noloop = [];
noloop = await looper2(json_array1);
console.log("db id returns",noloop)
console.log("no loop",noloop[0]); //im getting this undefined
function looper2(){
return new Promise(function(resolve, reject) {
// Do async job
for(var i=0;i<json_array1.length;i++){
var sl = "select id from json where name ="+db.escape(json_array1[i]);
db.query(sl, function(err,result) {
if (err) throw err;
console.log("individual id:", result)
noloop.push(result)
});
}
resolve(noloop)
});
}
The problem is that in looper2 function you resolve immediately after the forLoop and don't wait for the db queries to complete. You should instead resolve after the db queries have all completed
function looper2(){
return new Promise(function(resolve, reject) {
// Do async job
for(var i=0;i<json_array1.length;i++){
var sl = "select id from json where name ="+db.escape(json_array1[i]);
db.query(sl, function(err,result) {
if (err) throw err;
console.log("individual id:", result)
noloop.push(result)
if(noloop.length == json_array1.length) {
resolve(noloop)
}
});
}
});
}
You can't use await without async function. So, you have to create a self-executable function to use await like below. And if you are using promise inside a loop there is promise.all function available, you can use to resolve all pending promises.
(async () => {
var noloop = [];
noloop = await looper2(json_array1);
console.log(noloop);
})();
function async looper2(){
const promises = [];
for(var i=0;i<json_array1.length;i++){
var sl = "select id from json where name ="+db.escape(json_array1[i]);
db.query(sl, function(err,result) {
if (err) throw err;
promises.push(new Promise(result));
});
}
return Promise.all(promises)
}
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);
})