I'm using mongoose with an async/await function to query the DB. A few calls to the api doesn't return the necessary data. Below is the controller code
exports.GetAllUrls = async function(req, res, next){
try {
var urlsArray = [];
await Url.find({uid: req.params.uid}, function (err, urls) {
urls.forEach(function(url){
urlsArray.push(url);
console.log(url);
});
});
console.log("Await called");
return res.status(200).json({reply: urlsArray});
} catch(err) {
console.log(err);
}
}
There are a few times where the "Await called" is logged before the url data is logged.
Node Request Log:
Await called
GET /api/screens/GetAllUrls/0WyaS0ePeaS54zz9cAgCUnxoE1i1 200 31.348 ms - 12
{ _id: 5b0ad7effa8e80800153fa04,
url: 'https://yahoo.com',
timestamp: 2018-05-27T16:31:10.638Z,
uid: '0WyaS0ePeaS54zz9cAgCUnxoE1i1',
__v: 0 }
As seen in the logs, the function seems to proceed before the await function is called, but my understanding is that execution is paused until the await is completed and returned. Anyone have any ideas why this is happening?
I don't know the specifics on that library or method, but I can tell you why it's not working.
"await" will pause only when the right hand side of the statement returns a "Promise" object. In your code example, it seems that the function takes a callback. Callbacks, though asynchronous, are not promises. Perhaps you can check that library's API docs to see if it can return a Promise instead of taking the callback?
You are mixing callbacks with async-await. Don't do that. Please study how they work: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Your route handler should be:
exports.GetAllUrls = async function(req, res, next){
try {
const urlsArray = await Url.find({uid: req.params.uid}).exec()
return res.status(200).json({reply: urlsArray});
} catch(err) {
console.log(err);
}
}
.find() returns a Query object: http://mongoosejs.com/docs/api.html#find_find
.exec() returns a Promise: http://mongoosejs.com/docs/api.html#query_Query-exec
Related
How to use the asynchronous Await in Node.js by using these function and how
The request.get() function returns a Promise by which user will await...
I have tried the Below code so far and also gave the explanation below
async function fun1(req, res){
let response = await request.get('http://localhost:3000');
if (response.err) { console.log('error');}
else { console.log('fetched response');
}
The code above basically asks the javascript engine running the code to wait for the request.get() function to complete before moving on to the next line to execute it. The request.get() function returns a Promise for which user will await . Before async/await, if it needs to be made sure that the functions are running in the desired sequence, that is one after the another, chain them one after the another or register callbacks.
async function fun1(req, res){
let response = await request.get('http://localhost:3000');
if (response.err) { console.log('error');}
else { console.log('fetched response');
}
request package does not use return promise. Use the request-promise package which wraps the request with Promise.
You can use it like:
const rp = require('request-promise')
async function getSomeData() {
try {
const url = 'http://some.com'
// waits for promise to resolve
const data = await rp(url)
// data contains resolved value if successfull
// continue some other stuff
...
} catch (e) {
// handle error if error occurred
console.error(e)
}
}
I am trying to make res.locals.info available on every single page.
I'm trying to do this by middleware but I'm getting an error.
Apparently res.locals.info is not ready yet when the page render, thus I get an error info is not defined. How do I solve this?
app.use(function(req,res,next){
async function getInfo(user) {
let result = await info.search(user);
setInfo(result);
}
function setInfo(result){
res.locals.info= result;
}
getInfo(req.user);
return next();
})
search():
module.exports.search= function (user) {
var query=`SELECT count(*) as Info from dbo.InfoUsers WHERE user= '${user}' ;`
return new Promise((resolve, reject) => {
sequelize
.query(`${query}`, {model: InformationUser})
.then((info) => {
resolve(info);
})
})
};
You were calling next() before your getInfo() function had done its work, thus res.locals.info had not yet been set when you were trying to use it.
An async function returns a promise. It does NOT block until the await is done. Instead, it returns a promise immediately. You will need to use await or .then() on getInfo() so you know when it's actually done.
If info.search() returns a promise that resolves to the desired result, then you could do this:
app.use(function(req,res,next){
// this returns a promise that resolves when it's actually done
async function getInfo(user) {
let result = await info.search(user);
setInfo(result);
}
function setInfo(result){
res.locals.info= result;
}
// use .then() to know when getInfo() is done
// use .catch() to handle errors from getInfo()
getInfo(req.user).then(result => next()).catch(next);
});
And, you can remove the deferred anti-pattern from your search function and fix the error handling (which is a common issue when you use the anti-pattern). There is no need to wrap an existing promise in another promise.:
module.exports.search = function (user) {
var query=`SELECT count(*) as Info from dbo.InfoUsers WHERE user= '${user}' ;`
// return promise directly so caller can use .then() or await on it
return sequelize.query(`${query}`, {model: InformationUser});
};
I am experimenting with Express and MongoDB, and have a functional API server that can add, update, delete, and retrieve a single post. The issue I have run into is returning all of the documents from Mongo.
I have a GET route that outputs the results, except it does not behave as I imagined it would. When you run this, the first GET request to /notes returns and empty array, i.e. []
let notes =[];
app.get('/notes', (req, res) => {
async function getNotes() {
try {
await db.collection('notes').find().forEach(function (myDoc) {
notes.push(myDoc);
})
} catch(err) {
console.log(err)
}
console.log(notes);
res.send((notes));
}
getNotes();
});
On the second GET to /notes, however, the data that was pushed into notes[] is returned, and it is then overwritten by the newly pushed data.
Can anyone help me fill in the blank spot in my understanding of this? I imagine there is something that I just didn't understand along the way.
Edit***
I have experimented with this a bit, and am still running into the same issues.
let array= [];
async function getNotes() {
try {
await db.collection('notes').find().toArray(function (err, notesArray) {
array = notesArray;
})
} catch (err) {
console.log(err)
}
console.log(array);
return array;
}
app.get('/notes', (req, res) => {
getNotes();
res.send(array);
});
MongoDB's .toArray() won't both invoke a callback and return a Promise.
Returns:
Promise if no callback passed
And, await depends on the Promise, so you won't be able to use the callback as well.
Though, one isn't really necessary. When the promise resolves, await will return the array of documents, allowing you to assign that to array or any other variable.
try {
array = await db.collection('notes').find().toArray();
}
First, I'm sorry for the title, I couldn't mind up something better.
I thought I understand Node.js / KOA, at least the basics but now I'm starting to feel that I'm missing some fundamentals.
Take a look at the following code:
router.put("/",
parse,
async function (ctx, next) {
// do something
await next();
},
async function (ctx, next) {
// do something
await next();
},
async function (ctx, next) {
if (some condition) {
gm(imageBuffer)
.quality(80)
.write(profile_path, async function (err) {
gm(imageBuffer)
.resize(60, 60)
.quality(80)
.write(chat_path,async function (err) {
await next(); // HERE 1
});
});
} else {
await next();
}
// HERE 2
},
async function (ctx, next) {
responses.success(ctx, "Success");
}
);
So what this is all about. The ones that are familiar with KOA framework will immediately see what is going on here. Where my problem starts/ends is in the third async function. So what I'm trying to do here is some image manipulation (saving). gm is asynchronus, but as you can see from the code I'm using anonymous callback functions, and what I'm trying to achieve is that the last async function is being called when gm finishes through await next(); // HERE 1.
But what really happens is (from my understanding)... gm starts asynchronously.. and // HERE 2 is hit, and because there's nothing, end of function, KOA returns default 404 response. I simply can't understand why this is so and how to overcome this.
What I really want to happen is when callback finishes await next(); // HERE 1 gets called and I can return success response.
await next(); // HERE 1 of course gets called (eventually) but too late, because KOA already responds with 404.
If there is someone that is able and willing to explain what exactly is happening here, thank you.
As far as I see is that your aproach is not really following the async await pattern: The async function should the "await" the asynchronous part. This then needs to return a promise. So you have to encapulate your callback in a promise. Something like this could work (not testet, just to show the concept):
function gmAsync(){
return new Promise(function(resolve,reject){
gm(imageBuffer)
.quality(80)
.write(profile_path, async function (err) {
gm(imageBuffer)
.resize(60, 60)
.quality(80)
.write(chat_path,async function (err) {
resolve(....whatever....);
});
});
});
}
and then you async function could look like this:
async function (ctx, next) {
if (some condition) {
await gmAsync()
next()
} else {
await next();
}
},
...
Makes sense?
I have this function which gets some data from my database but i'm having a trouble calling the function and getting the proper response
function getEvents()
{
var x = [];
var l = dbCollection['e'].find({}).forEach(function(y) {
x.push(y);
});
return x;
});
and another function which calls this function but it always returns undefined.
How can i make the function wait till mongoose has finished filling up the array?
Thanks for the help! My life
dbCollection['e'].find is called non-blocking way so you are returning x before filling. You need to use callbacks or some mongoose promises. You can get all returning values from database like following snippet
function getEvents(callback) {
dbCollection['e'].find({}, function(error, results) {
// results is array.
// if you need to filter results you can do it here
return callback(error, results);
})
}
Whenever you need to call getEvents function you need to pass a callback to it.
getEvents(function(error, results) {
console.log(results); // you have results here
})
You should read mongoose docs for how queries work.
There is also support for promises in mongoose. You can check this url for more information about promises.
The solution proposed by #orhankutlu should work fine.
I will give another solution using promise. You can choose one between these two solutions depending on your style of programming.
Solution using promise:
function getEvents() {
return new Promise(function(resolve, reject){
dbCollection['e'].find({}, function(error, results) {
if (error) return reject(error);
var x = [];
results.forEach(function(y){
x.push(y);
});
// forEach() is a blocking call,
// so the promise will be resolved only
// after the forEach completes
return resolve(x);
});
});
};
Calling getEvents():
getEvents().then(function(result){
console.log(result); //should print 'x'
}).catch(function(err){
// Handle error here in case the promise is rejected
});
I will encourage you to try both the approaches, ie, using callbacks and using promises. Hope you find it useful!