Hello I am having the following problem with my API.
I am calling a function in my models from my service like so,
async getAccounts(/*offset = 0, limit = 500,*/ res) {
try {
let offset = 0;
let limit = 500;
let result = await this.model.getAllAccounts(offset, limit);
console.log(result, "Debugger 3");
return res.status(200).json(result);
} catch (error) {
logger.error('AccountsService::getAccounts', 39);
logger.error(error);
return errorHandler(error, res);
}
}
And the function in my models class looks something like this.
async getAllAccounts(offset, limit) {
// Need to add offset and limit parameters //done
console.log("URL",url);
MongoClient.connect(url, function(err, db) {
if (err) throw err;
logger.log('Query Accounts');
const dbo = db.db("DB_NAME");
dbo.collection('accounts').find({}).skip(offset).limit(limit).toArray(function(err, result) {
if (err) throw err;
db.close();
logger.log("Debugger 2xxx: ",result);
return result;
});
});
}
The problem I am having is that before Debugger 3's output comes before Debugger 2's output.
I think I need to await the result being returned from MongoDB but not sure how to do it. Could anyone please guide me?
await needs a promise for async to work. Currently, since your function returns undefined (as all functions without an explicit return), await is getting a promise of undefined, which is immediately resolved, and no waiting is necessary. On the other hand, the dbo uses a callback, not a promise; you cannot return a result from an asynchronous callback.
You should do something like this (not tested though):
async getAllAccounts(offset, limit) {
// Need to add offset and limit parameters //done
console.log("URL",url);
return new Promise(function(resolve, reject) {
MongoClient.connect(url, function(err, db) {
if (err) throw err;
logger.log('Query Accounts');
const dbo = db.db("DB_NAME");
dbo.collection('accounts').find({}).skip(offset).limit(limit).toArray(function(err, result) {
if (err) throw err;
db.close();
logger.log("Debugger 2xxx: ",result);
resolve(result);
});
});
});
}
If you're using the mongo db driver following versions 2.x, ie all version 2.x, 3.x, MongoClient.connect return a promise if no callback is passed as argument.
So you can do the next thing :
async getAllAccounts(offset, limit) {
// Need to add offset and limit parameters //done
try {
console.log("URL", url);
let db = await MongoClient.connect(url);
logger.log("Query Accounts");
const dbo = db.db("DB_NAME");
const result = await dbo
.collection("accounts")
.find({})
.skip(offset)
.limit(limit)
.toArray();
db.close();
logger.log("Debugger 2xxx: ", result);
return result;
} catch (e) {
throw e;
}
}
The reason why the getAllAccounts function call didn't await. Await will only work for a function that returns a promise. Here getAllAccounts doesn't return a promise, so wrap the method with a promise.
modul.exports.getAllAccounts = (offset, limit)=> {
// Need to add offset and limit parameters //done
return new Promise((resolve,reject)=>{
console.log("URL",url);
MongoClient.connect(url, function(err, db) {
if (err){
reject(err);
return;
}
logger.log('Query Accounts');
const dbo = db.db("DB_NAME");
dbo.collection('accounts').find({}).skip(offset).limit(limit).toArray(function(err, result) {
if (err){
reject(err);
return;
}
db.close();
logger.log("Debugger 2xxx: ",result);
resolve(result);
});
});
});
}
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));
After searching through countless posts I don't understand why I'm still getting a Promise pending after my awaits. The code below should explain it but I'm trying to pull a MongoDB query of the max value of a column/schema. The console.log within the function is giving me the correct timestamp but I'm trying to pass that out of the inner scope and function to another function.
This is pure NodeJS with only MongoDB imported. Can this be done without any external packages?
export async function getMaxDate() {
var time = MongoClient.connect(url, { useUnifiedTopology: true }, function (err, db) {
if (err)
throw err;
var dbo = db.db(getDB);
dbo.collection(getColl)
.find()
.limit(1)
.sort({ 'timestamp': -1 })
.toArray(function (err, result) {
if (err)
throw err;
time = result[0].time; // THIS IS GIVING THE CORRECT VALUE
console.log(time)
db.close();
});
});
return time
}
export async function getMax() {
var block = await getMaxDate();
return block
}
var t = getMax();
console.log(t); // THIS IS GIVE ME A PROMISE PENDING
getMax() returns a promise, you have to wait for it.
var t = await getMax()
Also getMaxDate uses an async callback that you want to promisify:
export async function getMaxDate() {
return new Promise((resolve,reject) => {
MongoClient.connect(url, { useUnifiedTopology: true }, function (err, db) {
if (err)
return reject(err);
var dbo = db.db(getDB);
dbo.collection(getColl)
.find()
.limit(1)
.sort({ 'timestamp': -1 })
.toArray(function (err, result) {
if(err)
reject(err);
else {
let time = result[0].time; // THIS IS GIVING THE CORRECT VALUE
console.log(time)
db.close();
resolve(time);
}
});
})
});
}
For reference, A is the same thing as B here:
async function A(x) {
if(x)
throw new Error('foo');
else
return 'bar';
}
function B(x) {
return new Promise((resolve,reject)=>{
if(x)
reject(new Error('foo'));
else
resolve('bar');
});
}
Promises came first, then async/await notation was introduced to make common Promise coding practices easier
You can use A and B interchangeably:
async function example1() {
try {
await A(1);
await B(0);
}
catch(err) {
console.log('got error from A');
}
}
async function example2() {
return A(0).then(()=>B(1)).catch((err)=>{
console.log('got error from B');
})
}
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 am attempting to return the result of a node-postgres query and store it in a variable. I can manage a console.log just fine, but cannot find a way to return the result so that it is accessible outside of the query method. I'm at a loss, and know I must be missing something obvious (or multiple obvious things), because if it isn't possible to do this I don't understand the point of node-postgres since all I will ever be able to do is log my results to the console.
I have tried the code below, along with a version using promises, and both receive the same result, 'undefined.' A console.log within the else works fine, but a return does not make the result accessible to the rest of the function. In the code below, my return comes back as 'undefined' as would a console.log in its place.
var selectFrom = function(data, table, condition) {
var queryResult;
pool.query(`SELECT ${data} FROM ${table} ${condition}`, function(err, result) {
if(err) { console.log(err); }
else { queryResult = result.rows[0][data]; }
})
pool.end();
return queryResult;
}
var result = selectFrom('amount','total_nonfarm_monthly_sa', `WHERE month='2019-08-31'`);
console.log(result);
This is what you are looking for:
async function selectFrom(data, table, condition) {
try {
const res = await pool.query(
`SELECT ${data} FROM ${table} ${condition}`
);
return res.rows[0][data];
} catch (err) {
return err.stack;
}
}
Outside of the previous function:
async function whateverFuncName () {
var result = await selectFrom('amount','total_nonfarm_monthly_sa', `WHERE month='2019-08-31'`);
console.log(result);
}
EDIT: I didn't run this code snippet but the main concept is pretty straightforward.
The "query" method is an async call, you should use async/await or Promise.
With async/await:
await client.connect()
const res = await client.query("SELECT amount FROM total_nonfarm_monthly_sa WHERE month='2019-08-31'");
console.log(res.rows[0]);
await client.end();
Edit:
I can see that there's an option of callback, but I would use the async/await
You need to use callbacks:
var selectFrom = function(data, table, condition, callback) {
pool.query(`SELECT ${data} FROM ${table} ${condition}`, function(err, result) {
if(err)
return callback(err);
callback(null, result.rows[0][data]);
})
pool.end();
}
selectFrom('amount','total_nonfarm_monthly_sa', `WHERE month='2019-08-31'`, function(err, result){
console.log(err, result);
});
or promises:
var selectFrom = function(data, table, condition) {
return new Promise(function(resolve, reject){
pool.query(`SELECT ${data} FROM ${table} ${condition}`, function(err, result) {
if(err)
return reject(err);
resolve(result.rows[0][data]);
})
pool.end();
});
}
selectFrom('amount','total_nonfarm_monthly_sa', `WHERE month='2019-08-31'`)
.then(function(result){
console.log(result);
}).catch(function(err){
console.log(err);
});
I'm new to NodeJS and after spending a few hours trying to understand how Promises work exactly, what seems to be an easy thing still doesn't work.
I'm trying to make a few calls to a database, and once all of those calls are done, do something else. What I have now is the following code, but none of the then-functions are called.
var queries = ['SELECT value FROM tag', 'SELECT url FROM download_link'];
console.log("Starting promises");
var allPromise = Promise.all([queryDatabaseAddToResult(connection, queries[0], result), queryDatabaseAddToResult(connection, queries[1], result)]);
allPromise.then(
function(result) {
console.log("1"); // Does not show up
}, function(err) {
console.log("2"); // Does not show up either
}
);
function queryDatabaseAddToResult(connection, query, result) {
return new Promise(function(resolve, reject) {
connection.query(query, function(err, rows, fields) {
if (err) {
console.log(err);
Promise.reject(err);
}
console.log(rows);
result.tags = JSON.stringify(rows);
Promise.resolve(result);
});
})
}
The calls to the database do get made, as logging the rows show up in the log.
The problem is that you are not calling the correct resolve and reject functions. It should be:
function queryDatabaseAddToResult(connection, query, result) {
return new Promise(function(resolve, reject) {
connection.query(query, function(err, rows, fields) {
if (err) {
console.log(err);
reject(err);
} else {
console.log(rows);
result.tags = JSON.stringify(rows);
resolve(result);
}
});
})
Note that the resolve and reject calls should not be scoped with Promise.. And you should have used an else to avoid calling resolve once you've called reject.
you have to do like this:
var promise1 = queryDatabaseAddToResult(connection, queries[0], result);
var promise2 = queryDatabaseAddToResult(connection, queries[1],result);
Promise.all([prromise1, promise2]).then(result => {
console.log(1);
}).catch(err => {
console.log(err);
});
return Promise.reject() //no need to add else part