node js await does not seem to wait - node.js

I am making a request to a mySQL Database and want to work with the returned results. I have the async function
async function doQuery(query) {
try {
console.log('0. connection start');
var connection = MySQL.createConnection({
host: "xxxxxxxx",
user: "yyyyyyyy",
password: "zzzzzzzz"
});
console.log('1. Connection set up. Connecting...');
var queryResults;
await connection.connect(function(err) {
if (err) console.log(err);
console.log("2. Connected!");
connection.query(query, function(err, result) {
if (err) console.log(err);
queryResults = JSON.parse(JSON.stringify(result));
console.log("3. " + util.inspect(result, {depth: 4}));
console.log("3.5 " + util.inspect(queryResults, {depth: 4}));
connection.end();
});
})
console.log ("results before return:" + JSON.stringify(queryResults));
return queryResults;
}
catch (err){
console.log("async err: " + err);
}
}
The function is then called from another function:
function handle async(handlerInput) {
console.log("ExecuteDBSearchHandler");
var query = buildQuery(handlerInput);
try{
var resultsPromise = doQuery(query)
.then(results => {
console.log("4. final results: " + results);
const count = results.length;
var speechText = 'Abfrage Erfolgreich, es gab ' + count + " Ergebnisse.";
return handlerInput.responseBuilder
.speak(speechText)
.withSimpleCard('Abfrage Erfolgreich', speechText)
.withShouldEndSession(false)
.getResponse();
})
}
catch (err){
console.log("error:" + err);
}
}
This code is to be used in a nodejs Alexa skill. When the function runs, I would expect the outputs in the log to be ordered ascending. The output I consistently get is 0, 1, 4, 2, 3, 3.5. The .then part is executed before the actual connection is established and the database queried.
Instead of the .then, I tried a simple await before, making the line
var results = await doQuery(query);
This lead to the exact same result. I am at a loss as to why await is infact not waiting. As a sidenote: While inside the block
connection.query(query, function (err, result) {
if (err) console.log(err);
queryResults = JSON.parse(JSON.stringify(result));
console.log("3. " + util.inspect(result, {depth: 4}));
console.log("3.5 " + util.inspect(queryResults, {depth: 4}));
connection.end();
});
queryResults shows results (in the debug log step 3.5). In the line
console.log ("results before return:" + JSON.stringify(queryResults));
it returns undefined. I tried so many things to get the results to the function scope of doQuery, but I can't seem to achieve that. Does the await fail due to this? And how can I fix it? I have read upwards of a dozen articles and docs about async/await and promises, yet I can't figure this out.

You need to wrap your callback function in Promise to make use of await keyword
For example:
async function dbQuery(query) {
try {
const connection = MySQL.createConnection({
host: "xxxxxxxx",
user: "yyyyyyyy",
password: "zzzzzzzz"
});
return new Promise(function(resolve, reject) {
connection.connect(function(err) {
if (err) reject(err);
connection.query(query, function (err, result) {
if (err) reject(err);
connection.end();
resolve(JSON.parse(JSON.stringify(result)));
});
});
});
} catch {
console.error("async err: " + err);
}
}
The one can also use util.promisify to wrap callback in promise

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));

Can't work out promises to return values - NodeJS

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');
})
}

API does not wait for await

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);
});
});
});
}

Different reponses for mongodb query using callback and promise

Have you any idea why I don't get any error in the first example? But if I use a callback, I get MongoTimeoutError.
await server.stop();
try {
const r = await db.things.insertOne({ a: 1 }); // no error, r is undefined
assert(!r);
} catch (err) {
console.log(err);
}
await server.restart();
const r = await db.things.insertOne({ a: 1 });
assert(r.insertedCount === 1);
db.things.insertOne({ a: 1 }, (err, result) => {
if (err) {
//"MongoTimeoutError" "Server selection timed out after 10000 ms"
return reject(err);
}
resolve(result);
})
In options I have bufferMaxEntries = 0.
There is an issue with mongodb driver. See the JIRA ticket

Node js nested async call

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.

Resources