async/await is not working for mongo DB queries - javascript

Working case:
async await is working fine when we call a asynchronous function and that function returning a promise resolve()
Not working case:
async await is not working for mongo DB queries
tried then(), async/await
I have 2 JS files.
In one.js file i am importing function which is in functionone.js
WORKING CASE:
When one.js looks like
var functiononestatus = transactions.functionone(req.session.email).then((came) => {
console.log(came); // getting `need to be done at first` message
console.log("exec next")
});
When functionone.js looks like
module.exports.functionone = functionone;
async function functionone(email) {
return await new Promise((resolve, reject) => {
resolve('need to be done at first')
});
});
NOT WORKING CASE (when mongo db query need to be executed):
When one.js looks like
var functiononestatus = transactions.functionone(req.session.email).then((came) => {
console.log(came); // getting undefined
console.log("exec next")
});
When functionone.js looks like
module.exports.functionone = functionone;
async function functionone(email) {
//mongo starts
var collection = await connection.get().collection('allinonestores');
await collection.find({
"email": email
}).toArray(async function(err, wallcheck) {
return await new Promise((resolve, reject) => {
resolve(wallcheck[0])
});
});

Quick clarification:
.collection('name') returns a Collection instance, not a Promise, so no need to await for it.
toArray() operates in two modes: either with a callback when a function is provided, either returns a Promise when no callback function is provided.
You're essentially expecting a Promise result out of toArray() while supplying a callback function, resulting in undefined, because callback takes priority and no promise is returned, due to the dual operation mode of toArray().
Also, toArray(callback) does not take an async function as callback.
Here's how your code should look like, for retrieving a collection:
const client = await MongoClient.connect('your mongodb url');
const db = client.db('your database name'); // No await here, because it returns a Db instance.
const collection = db.collection('allinonestores'); // No await here, because it returns a Collection instance.
and then, code for fetching results:
const db = <get db somehow>;
// You could even ditch the "async" keyword here,
// because you do not do/need any awaits inside the function.
// toArray() without a callback function argument already returns a promise.
async function functionOne(email) {
// Returns a Collection instance, not a Promise, so no need for await.
const collection = db.collection('allinonestore');
// Without a callback, toArray() returns a Promise.
// Because our functionOne is an "async" function, you do not need "await" for the return value.
return collection.find({"email": email}).toArray();
}
and code alternative, using callback:
const db = <get db somehow>;
// You could even ditch the "async" keyword here,
// because you do not do/need any awaits inside the function.
// You're forced to return a new Promise, in order to wrap the callback
// handling inside it
async function functionOne(email) {
// Returns a Collection instance, not a Promise, so no need for await.
const collection = db.collection('allinonestore');
// We need to create the promise outside the callback here.
return new Promise((resolve, reject) => {
db.find({"email": email}).toArray(function toArrayCallback(err, documents) {
if (!err) {
// No error occurred, so we can solve the promise now.
resolve(documents);
} else {
// Failed to execute the find query OR fetching results as array someway failed.
// Reject the promise.
reject(err);
}
});
});
}

Note: First of all i really need to thank #mihai Potra for the best answer.
Here we go
Case 1:
If it is a function which need to find documents and return from MongoDb as mihai mentioned, below answer is uber cool
const db = <get db somehow>;
async function functionOne(email) {
const collection = db.collection('allinonestore');
return collection.find({"email": email}).toArray();
}
case 2:
If there are nested functions which need to return values every time below ans will be the best as of my knowledge
-no need async/await keywords for every function or no need then()
function one(<parameter>) {
return new Promise(function(resolve, reject) {
const collection = connection.get().collection('<collection_name>');
const docs = collection.find({"Key": Value_fromFunction}).toArray( function (err, result) {
resolve(result[0]);
});
That's it, use resolve callback when ever it needed.

Related

How to get data from async functions and after getting data allow the next code to execute

I get some data from mongodb using mongoose find() and perform some validation on that data, but the problem is that this function is async and it does not wait for the data to be completed and execute the next code.
and when the next code is executed it enables to perform because it has null data. then i wrote my validation logic in the async function so that when data is available only then it move to next code but on every return it sends undefined data.
function isValidObject(obj) {
schemaData.find({}, (error, data) => { // calls data from db
var contactSchema = data; // this is data i need
if(//validation fails){
return "wrong data";// every time it returns undrfined on every
// condition
}
});
}
var validationResp = isValidObject(obj);
console.log(validationResp); // output is undefined
i also used "await" to wait for the data, but in that case it return [object promise] on every return statement
use async/await
In your case:
async function isValidObject(obj) {
let data = await schemaData.find({}); // wait until it resolve promise
//do your validaion
return data;
}
isValidObject(obj).then((validationResp)=>{console.log(validationResp)});
use the then() method it return a promise
var promise1 = new Promise(function(resolve, reject) {
resolve('Success!');
});
promise1.then(function(value) {
console.log(value);
// expected output: "Success!"
});
more details at MDN Using the then method
Are you familiar with "promises".You can use ".then(callback)". It will wait until async function is executed. And you can perform your validation in the callback function
User.findById(id)
.then(function(user) {
if (!user) {
return "here you return an error";
}
return "you return OK with payload from database";
})
.catch(next);
What is happening in your case is that when you assign var validationResp = isValidObject(obj); the function isValidObject(obj) has not returned anything and has only scheduled a callback(This is a very important concept when working with callbacks). As pointed out above you need to use Promises. The below is an example of your case as to how you can use Promises.
function isValidObject(obj){
return new Promise((resolve,reject) => {
schemaData.find({}, (error, data) => { // calls data from db
if(validation fails){
reject(error)// every time it returns undrfined on every
// condition
}else{
var contactSchema = data; // this is data i need
resolve(data)// You can resolve the promise with the data directly instead
// of assigning it to a variable or you can use (resolve(contactSchema))
}
})
})
}
After this when you want to use the data you can use do something like the below code snippet.
isValidObject(obj)
.then(result => {
// Use the result object here. This is the result from the Database
})
.catch(error => {
//Handle the Error
})

Node- Append file in Async function

I'm using mongoose to query a database for objects and wish to write each object to a file. The console.log shows me that the data I want is being returned from the query, but the file that is created by fs.append (./returned.json) is always empty. Is it not possible to do this within an async function?
async function findReturned(){
try {
const returned = await Data.find({});
returned.forEach(function(file) {
returnedfiles = file.BACSDocument;
console.log(returnedfiles);
fs.appendFile('./returned.json', returnedfiles, 'utf-8', (err) => {
if (err) throw err;
});
});
process.exit();
} catch(e) {
console.log('Oops...😯😯😯😯😯😯😯');
console.error(e);
process.exit();
}
};
You probably should use mz/fs for async function
const fs = require('mz/fs');
...
returned.forEach(async (file) => {
returnedfiles = file.BACSDocument;
await fs.writeFile('./returned.json', returnfiles, 'utf-8');
})
You don't need to throw the err as it will the catch.
You can't mix async function and functions with callback API
Also you can't call multiply async function inside forEach, you have to create a promise that will do what do you need in chain (see reduce function in example below).
Another way - to use .map, return an array of promises and await it using
await Proimse.all(arrayOfPromises)
but in this case the order of new data in file will not be as the order in the initial array.
In order to do that you need to promisify fs.appendFile and call promisified version of function
// add new import
const {promisify} = require('util');
// create new function with Promise API on existing Callback API function
const appendFile = promisify(fs.appendFile)
async function findReturned(){
try {
const returned = await Data.find({});
// Create a single promise that will append each record one by one to the file in initial order.
const promise = returned.reduce(function(acc, file){
returnedfiles = file.BACSDocument;
console.log(returnedfiles);
acc.then(() => appendFile('./returned.json', returnedfiles, 'utf-8'));
return acc
}, Promise.resolve());
// Wait until the promise resolves;
await promise;
process.exit();
} catch(e) {
console.log('Oops...😯😯😯😯😯😯😯');
console.error(e);
process.exit();
}
};
Instead of using build int fs module you can use fs-extra module that already has promisified versions of native methods and several extra methods.
What you can improve in your function - do not append file multiply times, but collect all data that you need to append to file into single variable and do it once.
If for some reason you have to do it multiply times - obtain file descriptor using fs.open and pass it instead of file name to fs.appendFile (or promisified verison). In this case do not forget co close file descriptor (fs.Close).

Why do i get undefined, even when using a promise?

I am trying to use a promise to call a function getTweets.
Not using an AJAX call, but a simple promise 'call' from 1 javascript file to another.
The function works, but i keep getting 'undefined'.
I have read dozens of questions here on stackoverflow and have spent days
to understand promises, but still can't solve it.
var Twit = require('twit') // Imports the Twitter library
require('dotenv').config() // to get the environment vars into the app
// This is the function:
function getTweets (screen_name) {
let T = new Twit({ /* <twitter key and token here> */ });
T.get('statuses/user_timeline', { screen_name: screen_name, count: 3}, function (err, data, response) {
let myTweets = [];
for (let i in data) {
let text = data[i].text;
myTweets.push(text);
}
return myTweets;
})
}
module.exports.getTweets = getTweets;
And this is the promise that tries to get the tweets:
var promise = tweets.getTweets('dizid');
promise.then(
console.log(myTweets),
console.log(err))
// gives error: promise.then(
// ^
// TypeError: Cannot read property 'then' of undefined
Any help greatly appreciated.
Your problem is that you never return anything from your getTweets() function even though it needs to return a promise. The function calls T.get() and pass it a callback function. You return from this callback function but this doesn't do anything, it doesn't mean that this value gets returned from getTweets().
This is a pretty common mistake when it comes to working with asynchronous calls. What needs to be done is to make getTweets() return a promise that gets resolved when it should.
When working with asynchronous calls that don't implement the promise interface, you need to wrap this call with a new promise. Your getTweets() function should then look like this:
function getTweets (screen_name) {
let T = new Twit({ /* <twitter key and token here> */ });
return new Promise(function(resolve, reject) {
T.get('statuses/user_timeline', { screen_name: screen_name, count: 3}, function (err, data, response) {
if (err) {
reject(err); // Reject the promise since there was an error
} else {
let myTweets = [];
for (let i in data) {
let text = data[i].text;
myTweets.push(text);
}
resolve(myTweets); // Resolve the promise with the result
}
});
});
}
However, it seems the Twit API does support the promise interface, so instead of providing a callback function you can just use the promise created by T.get(). HMR's answer explains how to do this.
Another mistake you've made is with this code:
promise.then(
console.log(myTweets),
console.log(err))
The way you've written it, it reads "Run console.log(myTweets) and console.log(err), then invoke promise.then() with the result of the former as the first argument and the result of the latter as the second argument.
then() takes callback functions (which get invoked depending on the resolving/rejection of the promise) as arguments, so the code should look like this:
promise.then(
function(myTweets) {
console.log(myTweets);
},
function(err) {
console.log(err);
});
Async/await
If you're interested in taking things further, the modern approach to working with asynchronous code is async/await, which is syntactic sugar for promises that lets you write asynchronous code more similar to regular synchronous code.
A function marked as async will implicitly return a promise, but you write it as if you return a regular value. Using the await keyword inside an async function will implicitly wait for a promise to resolve and unwrap the resolved value. The main practical benefits of this is that you can use asynchronous calls in loops and handle errors with regular try-catch blocks. Your getTweets() function would look like this using async/await:
async function getTweets(screen_name) {
let T = new Twit({ /* <twitter key and token here> */ });
const data = await T.get('statuses/user_timeline', { screen_name: screen_name, count: 3});
// Let's also use map() instead of a for loop
let myTweets = data.map(function(item) { return item.text; });
return myTweets;
}
Since get seems to return a promise you don't need to use a callback. Get Tweets can look something like this:
// in getTweets
return T.get(
'statuses/user_timeline',
{ screen_name: screen_name, count: 3}
).then(
function (data) {
console.log("data:",JSON.stringify(data,undefined,2));
return data.map(item=>item.text);
}
)
// exports the function getTweets so that other modules can use it
module.exports.getTweets = getTweets;
If that didn't work please let us know what the output of the program is (update question).
You can call getTweets like so:
tweets.getTweets('dizid')
.then(
myTweets=>
console.log(myTweets),
err=>
console.log(err)
)
I think you forget add function like
promise.then(function(res){
//code
}
Your .then() should include a call back function.
promise.then( res => {
console.log(res);
});
edit: I'm using an ES6 syntax for arrow functions, in case you're new to that.

Issue with Nodejs promise

I have a sequence of function calls, connected with ES6 promises. Apparently, there is something wrong with this implementation, as API calls to the endpoint are not returning anything and the browser is stuck waiting for a response.
Please advise.
module.exports.insertTreatmentDetails = function (req, res) {
var doctorId = 10000
var departmentId = 10000
var procedureid = 10000
var hospitalSchema = new hospitalModel();
var p = new Promise(function (resolve, reject) {
counterSchema.getNext('Treatment.doctor.doctorId', collection, function (doctorId) {
doctorId = doctorId;
})
counterSchema.getNext('Treatment.departmentId', collection, function (departmentId) {
departmentId = departmentId
})
counterSchema.getNext('Treatment.procedureid', collection, function (procedureid) {
procedureid = procedureid
})
}).then(function () {
setData()
}).then(function (){
hospitalSchema.save(function (error, data) {
if (error) {
logger.error("Error while inserting record : - " + error)
return res.json({ "Message": error.message.split(":")[2].trim() });
}
else {
return res.json({ "Message": "Data got inserted successfully" });
}
});
});
};
The short answer is that you aren't calling resolve or reject inside the first promise in your chain. The promise remains in a pending state. Mozilla has a good basic explanation of promises.
How to Fix
It appears that you want to retrieve doctorId, departmentId, and procedureId before calling setData. You could try to wrap all three calls in one promise, checking whether all three have returned something in each callback, but the ideal is to have one promise per asynchronous task.
If it's feasible to alter counterSchema.getNext, you could have that function return a promise instead of accepting a callback. If not, I would recommend wrapping each call in its own promise. To keep most true to what your code currently looks like, that could look like this:
const doctorPromise = new Promise((resolve, reject) =>
counterSchema.getNext('Treatment.doctor.doctorId', collection, id => {
doctorId = id;
resolve();
}));
Then you could replace the first promise with a call to Promise.all:
var p = Promise.all([doctorPromise, departmentPromise, procedurePromise])
.then(setData)
.then(/* ... */);
Promises allow you to pass a value through to the next step, so if you wanted to get rid of your broadly-scoped variables (or set them in the same step where you call setData), you could just pass resolve as your callback to counterSchema.getNext and collect the values in the next step (also how you'd want to do it if you have counterSchema.getNext return a promise:
Promise.all([/* ... */])
.then(([doctorID, departmentID, procedureID]) => {
// If you aren't changing `setData`
doctorId = doctorID;
departmentId = departmentID;
procedureid = procedureID;
setData();
// If you are changing `setData`
setData(doctorID, departmentID, procedureID);
}).then(/* ... */).catch(/* I would recommend adding error handling */);

Resolving async function within internal callback

When explicitly returning a new Promise from a function we get resolve and reject functions passed in. We can use these methods inside our function at any point to indicate the resolution or rejection of the promise, including inside callbacks to other functions internally.
Consider:
const doSomeDbWork = function (db) {
return new Promise((resolve, reject) => {
db.doStuff(result => resolve(result));
});
};
Is there an equivalent when using async without explicitly returning a new Promise when we can't also await (like in the case of this db callback)?
const doSomeDbWork = async function (db) {
db.doStuff(result => /* ? */);
};
I'm currently thinking that this isn't possible and that I just need to return a new Promise. Have I missed a trick? Is there a way to do this?
Is there an equivalent when using async without explicitly returning a new Promise.
No. async/await only works with promises (or thenables), and you have to promisify everything else yourself. In your case, that would most likely be something like
async function doSomeDbWork(db) {
return new Promise(db.doStuff);
}
or
async function doSomeDbWork(db) {
return new Promise(resolve => { db.doStuff(resolve); });
}
(possible with an await after the return).

Categories