await doesn't wait for function to end - node.js

I know this has been asked a lot but I can't seem to use the existing answers to get my code to work. I am trying to use mongojs to make a single query, then put the results in a global (relative to the scope) variable to avoid nesting multiple callbacks. However, I can't seem to get the code to wait for the query to end before continuing.
async function taskmaster() {
const db = mongojs(mongoUri.tasker);
let currentDoc;
const getTask = async function() {
db.tasks.findOne({'task_id': {'$in': [taskId, null]}}, function(err, doc) {
console.log(doc);
currentDoc = doc;
});
}
await getTask();
console.log(currentDoc);
// Code to process currentDoc below
}
No matter what I do, console.log(doc) shows a valid MongoDB document, yet console.log(currentDoc) shows "undefined". Thanks in advance!

Inside your async function, you use findOne method() in callback style, so it's totally normal that console.log(currentDoc) shows undefined, because it executes before
currentDoc = doc;
You can promisify the findOne method, to use it with async/await keyword.
I found a tutorial to promisfy a callback style function here, hope it help : https://flaviocopes.com/node-promisify/
--- EDIT ---
I rewrite your code when promising the findOne method, as suggested by O.Jones
async function taskmaster() {
const getTask = async (taskId) => {
return new Promise((resolve, reject) => {
db.tasks.findOne({'task_id': {'$in': [taskId, null]}}, function(err, doc) {
if(err) {
console.log("problem when retrieve data");
reject(err);
} else {
resolve(doc);
}
});
})
const db = mongojs(mongoUri.tasker);
const currentDoc = await getTask(taskId);
console.log(currentDoc);
}

Related

NEDB NodeJS find document and wait for the result

I am using NEDB for some local storage in an NodeJS Application. Therefore I do have an handlerscript "nedbhandler.js" which I do require in my main.js.
var NEDB = require('./nedbhandler.js');
async function test(){
var value = await NEDB.getValue_byID(1);
console.log(value)
}
test()
while in the nedbhandler.js is my query handled like this:
async function getValue_byID(id){
db.config.findOne({ _id: id }, function (err, doc) {
callback(doc);
});
function callback(doc) {
console.log(doc)
return doc;
}
}
exports.getValue_byID = getValue_byID;
While the console from nedbhandler.js logs the expected value, the main.js still is undefined.
What would be the best practice to load all config querys before loading next function in the main.js?
You want your getValue_byID function to return a promise
function getValue_byID(id){
return new Promise((resolve, reject) => {
db.config.findOne({ _id: id }, (err, doc) => {
err ? reject(err) : resolve(doc);
});
});
}
Note how I changed the function keyword to the more modern => syntax.
It is also considered better practice to use const and let insteadof var
There is a wrapper module for nedb, called NeDB-promises. It does more or less what I have done here, so you can use async/await instead of callbacks.

Async - Await issue using Twitter API

I'm currently trying to practice using an API with Twitter API.
I'm using Twit package to connect to twitters API but when I try to do a get request I get
Promise { pending }
I have tried using Async-Await but I'm not sure what I'm doing wrong here.
Here is my code:
const Twit = require('twit');
const twitterAPI = require('../secrets');
//Twit uses OAuth to stablish connection with twitter
let T = new Twit({
consumer_key: twitterAPI.apiKey,
consumer_secret: twitterAPI.apiSecretKey,
access_token: twitterAPI.accessToken,
access_token_secret: twitterAPI.accessTokenSecret
})
const getUsersTweets = async (userName) => {
let params = { screen_name: userName, count: 1 }
const userTweets = await T.get('search/tweets', params, await function (err, data, response) {
if (err) {
return 'There was an Error', err.stack
}
return data
})
return userTweets
}
console.log(getUsersTweets('Rainbow6Game'));
Problem
The biggest assumption that is wrong with the sample code is that T.get is expected to eventually resolve with some data.
const userTweets = await T.get('search/tweets', params, await function (err, data, response) {
if (err) {
return 'There was an Error', err.stack
}
return data // 'data' returned from here is not necessarily going to be received in 'userTweets' variable
})
callback function provided as last argument in T.get function call doesn't have to be preceded by an 'await'.
'data' returned from callback function is not necessarily going to be received in 'userTweets' variable. It totally depends on how T.get is implemented and can not be controlled.
Reason
Thing to be noted here is that async / await works well with Promise returning functions which eventually get resolved with expected data, however, that is not guaranteed here
Relying on the result of asynchronous T.get function call will probably not work because it returns a Promise { pending } object immediately and will get resolved with no data. The best case scenario is that everything with your function will work but getUsersTweets function will return 'undefined'.
Solution
The best solution is to make sure that your getUsersTweets function returns a promise which eventually gets resolved with correct data. Following changes are suggested:
const getUsersTweets = (userName) => {
return new Promise ((resolve, reject) => {
let params = { screen_name: userName, count: 1 }
T.get('search/tweets', params, function (err, data, response) {
if (err) {
reject(err);
}
resolve(data);
})
}
}
The above function is now guaranteed to return expected data and can be used in the following way:
const printTweets = async () => {
const tweets = await getUsersTweet(userName);
console.log(tweets);
}
printTweets();
From what I can see on your code, getUserTweets is an async function, so it will eventually return a promise. I'm assuming you will use this value on another function, so you will need to use it inside an async function and use await, otherwise you will always get a promise.
const logTweets = async (username) => {
try {
const userTweets = await getUsersTweets(username);
// Do something with the tweets
console.log(userTweets);
catch (err) {
// catch any error
console.log(err);
}
}
If logging is all you want and you wrapped it inside a function in which you console.log it, you can call that function directly:
logTweets('someUsername');

`find()` Not Working Like `findOne()` After Connecting to MongoDB Via Node

I am familiar with how to get documents from the Mongo shell, but am having difficulty getting documents using find() when connecting via Node.
What I'm getting right now looks like a lot of cursor info, but not the actual documents.
What do I need to change with the following code so that I get the actual documents logged to the console for 'results'?
const config = require('./../../../configuration');
const url = config.get('MONGO_URL');
const dbName = config.get('MONGO_DATABASE');
const MongoClient = require('mongodb').MongoClient;
const client = new MongoClient(url);
module.exports = async function getSchedules() {
let results;
return new Promise((resolve, reject) => {
client.connect(async function (err) {
if (err) return reject(err);
try {
const db = await client.db(dbName);
results = await db.collection('schedules').find();
} catch (error) {
return reject(error);
}
return resolve(results);
});
});
};
... And here's where I actually try and get the documents:
async function getSchedulesFromDB() {
await getSchedules().then((schedules => {
console.log('schedules: ', schedules); // expect result here
return schedules;
}));
}
When I used this same kind of code structure on a findOne(), it worked. But here when using a find() it is not. What am I missing? Does find() work fundamentally differently than findOne()?
Yes. find() returns a cursor over which you must iterate. findOne() returns a single doc, not a cursor. If you want an array of results, you must "build it yourself" by iterating the cursor, something like:
results = [];
db.collection('schedules').find().forEach(function(d) { results.push(d); });

Node.js Await Async with util class function

I am fairly new to Node.js and I am trying to pick it up with Koa.js framework.
I am struggling to understand why the index.js -> console.log run even if I have the await in the value.
Can anyone point me in the right direction?
index.js
router.get('getValue','/display',async (ctx) => {
var myUtilfunction = require('../util')
var result = await myUtilfunction.getData()
console.log(result)
}
util.js
async function getData(){
var customObject =[]
var standardObject =[]
conn.describeGlobal(function (err,res){
if (err){return console.error(err)}
console.log('No of Objects ' + res.sobjects.length)
res.sobjects.forEach(function(sobject){
if (sobject.custom){
customObject.push(sobject)
}else{
standardObject.push(sobject)
}
})
console.log("Done")
})
return [customObject, standardObject]
}
Try this one
await, works inside async functions
router.get('getValue','/display', async (ctx) => {
var myUtilfunction = require('../util')
var result = await myUtilfunction.getData();
console.log(result)
});
function getData(){
return new Promise((resolve, reject) => {
resolve('result goes here');
});
}
You need to specify the function is async for the await to work.
Something like this:
router.get('getValue','/display', async (ctx) => {
var myUtilfunction = require('../util')
var result = await myUtilfunction.getData()
console.log(result)
}
Hope this helps :)
In your function getData(), I think you've missplaced your return statement. The return statement should be placed inside the callback function used for conn.describeGlobal().
As you actually write your getData(), the conn.describeGlobal() call seems to be an asynchronous treatment, so the return statement placed outside is probably executed before you pushed something in your arrays.
The consequence is that your router get an empty response from your getData() function then the promise made by await keyword is resolved with an empty answer.

Node.js MongoDb find function ignores wait

I need the callback function of find from Node.js mongodb 3.1.6 to be triggered before the return statement of an async function, however the return statement is called before the callback function even-though there is a wait in front.
async function(myId) {
const myObject = MyObject()
await collection.find({where: {id: myId}}, async (err, results) => {
if (err) {
logger.error('error');
}
myObject.add(results);
});
return myObject
}
I have seen some examples where instead of find(query, callback) the pattern find(query).toArray() was used. But this doesn't run at all in my case. We use Node.js mongodb 3.1.6 with loopback-connector-mongodb maybe this is related to the problem.
If mongo doesn't provide a promise-answering function, then promisify this one yourself. Neither that promise-creating wrapper nor the anonymous callback it uses should be declared async, but the caller should....
function findById(collection, myId) {
return new Promise((resolve, reject) => {
collection.find({where: {id: myId}}, (err, results) => {
(err)? reject(err): resolve(results);
});
});
}
// now callers can use the async await pattern...
async someFunction() {
try {
let myId = // ...
let collection = // ...
let results = await findById(collection, myId);
// do something with results
} catch (err) {
// error
}
}
The key idea is that collection.find with the callback isn't eligible for await, because it doesn't return a promise. The anonymous callback function you pass to it isn't an async function... it does its work right away, as soon as find calls it back. So we build a promise around mongo, then use the new async/await syntax with that promise.

Resources