I have been trying to convert my existing Express JS code from function callback to Async Await.
The function callback code is
app.get('/contactlist', (req, res) => {
db.contactlist.find((err, docs) => {
res.json(docs);
});
});
And the Async Await code is
app.get('/contactlist', async (req, res) => {
db.contactlist.find(async(err, docs) => {
res.json(await docs);
});
});
The new code works good and I have strong feeling that, it is not the right Implementation of Async Await
You're still using callbacks. You can only switch to async/await, if the API returns promises. app.get() does not return promises, so the following doesn't work with Express:
let (req, res) = await app.get('/contactlist');
try {
let docs = await db.contactlist.find();
res.json(docs);
} catch (err) {
// Handle the error
}
All of that would have to be inside an async function.
What is db in your example? If db.contactlist.find returns a promise, you can use async/await like this:
app.get('/contactlist', async (req, res) => {
try {
let docs = await db.contactlist.find();
res.json(docs);
} catch (err) {
// Handle the error
}
});
you did not convert the callback to async await at all,callback is still there.The right implementation would look like this
app.get('/contactlist', async (req, res) => {
const docs = await getContactList();
res.json(docs)
});
function getContactList() {
let promise = new promise(function(resolve, reject) {
db.contactlist.find({}, function(err, results) {
if (err)
reject(err)
else
resolve(results)
});
});
return promise
}
^EDITS: Here's the complete example, if you have a custom function against await, you can promisify that function by creating and returning a promise in that function
Related
I wrote a small Javascript module to get rows from a MongoDB database:
async function getAll() {
await mongoose.connect(config.mongoURL).catch(err => { console.log(err); });
const conn = mongoose.connection;
conn.collection('healthdata')
.find({}).toArray().then(result => {
console.log('=>Docs:', result);
return result;
}).catch (err => {
console.log(err);
}).finally(() => {
conn.close();
});
}
exports.getAll = getAll
I get the correct display from the console ("=>Docs:").
But, when I want to get the result from the following calling function, I get the Undefined value ("Result:"):
app.get("/", async (req, res) => {
var docs = await db.getAll().then();
console.log("Result:", docs);
res.render("index.html");
});
What is missing? Wrong?
you should not mix promises with async/await, so either the answer provide by G-Force but remove the await before db.getAll() or use var docs = await db.getAll(); this should solve your problem.
Bro if you are using mongoose follow these to get data from mongoose schema
schema.find().exec().then(result=>{
console.log(result)
}).catch(error=>{
console.log(error)
})
here all schema data in result that we console if any error come it displays in console.log(error)
I think the console.log(Result:...) line is executing before your getAll completes.
I would wrap it inside the "then" clause:
app.get("/", async (req, res) => {
const docsReturned = await db.getAll()
.then(docs => {
console.log("Result:", docs);
res.render("index.html");
})
.catch(err => {
console.error(err);
}));
return(docsReturned);
I also suggest adding a catch for any errors...good practices. :)
Ok, so I know how to program in C# fairly well and I have started programming in JS recently (node js). To be honest I was in a bot of shock from async calls.
Let's say I have this code in C#:
var t_1 = SomeAsyncTask();
var t_2 = SomeOtherAsyncTask();
Task.WaitAll(t_1, t_2);
var res_1 = t_1.Result;
var res_2 = t_2.Result;
Is there a JS equivalent of this? So far I have managed this:
In User.js:
var express = require("express");
var router = express.Router();
var sqlDo = require("../../js_help/DatabasReq/sqlDo.js");
router.get("/", async function(req, res){
var json = sqlDo.ExecCommand("select * from Users");
res.send(json); //json.recordset
});
module.exports = router;
In sqlDo.js:
module.exports = {
ExecCommand: function(command){
// sql and config are defined before.
sql.connect(config, function () {
var request = new sql.Request();
request.query(command, function (err, recordset) {
if (err) console.log(err)
console.log(recordset.recordset);
return recordset;
});
});
}
};
My problem is that this code is running async. I have tried putting await to different places but nothing worked. So when I start my server it returns nothing. I can tell that it is completing a call because I let it read results into console.
Thanks for any help!
Btw: I have tried googling/stackoverlow-ing,.. But I was not able to find anything that would look like C# equivalent. Is it even possible to write it like in c#? Again thanks for every answer...
To make your ExecCommand function async, you have to make it return a Promise. Read about Promises for instance here
module.exports = {
ExecCommand: function(command){
return new Promise((resolve, reject) => { //return a Promise from the function
sql.connect(config, function () {
var request = new sql.Request();
request.query(command, function (err, recordset) {
if (err) {
reject(err); //if there is an error, reject the Promise
} else {
resolve(recordset); //if no error, resolve the Promise with the result
}
});
});
});
}
};
Depending on your SQL library, it may also support promises already, instead of callbacks
module.exports = {
ExecCommand: function(command) {
return sql.connect(config)
.then(() => {
return new sql.Request().query(command);
})
}
};
or with async/await
module.exports = {
ExecCommand: async function(command) {
await sql.connect(config);
return await new sql.Request().query(command);
}
};
Then you can call this function in the requesthandler either like this
router.get("/", async function(req, res){
try {
var json = await sqlDo.ExecCommand("select * from Users");
res.send(json);
catch (err) {
console.log(err);
res.sendStatus(500);
}
});
or like this
router.get("/", function(req, res){
sqlDo.ExecCommand("select * from Users")
.then(json => { //the promise resolved
res.send(json);
})
.catch(err => { //the promise rejected
res.sendStatus(500);
console.log(err);
});
});
I prefer the second variant. But that may be just my personal opinion ...
I have an API endpoint created using Node/Express. I want to call a local function from the controller function asynchronously, but the called local function is immediately returning undefined rather than the asynchronous results:
module.exports = {
sendMail: (req, res) => {
(async () => {
await testAsync("email","token").then(function(result){
console.log(result); //logs undefined
res.json("result=" + result);
});
})();
},
};
async function testAsync(email, token){
await setTimeout(function(){
return email;
}, 1000)
}
Well first remove the async IIFE you dont really need it. Then remove the .then() and go with a try / catch. Please dont use .then() with async / await you either go with .then() chaining or async / await but not both:
`
module.exports = {
sendMail: async (req, res) => {
try {
let result = await testAsync("email","token");
res.json("result=" + result);
}catch(err){
res.json(err);
}
},
};
Also await works on promises, so you need to return an promise here. You also dont need async here:
function testAsync(email, token){
return new Promise((resolve, reject)=>{
setTimeout(function(){
resolve("works");
}, 1000)
})
}
You need to wrap your setTimeout() in a Promise.
Like this:
async function testAsync(email, token) {
return new Promise((resolve) => {
setTimeout(function () {
resolve(email);
}, 1000)
});
}
I made an api like below.
I think it works asyncrously well without doubt.
exports.query = async(req, res) => {
try{
const result = await user.findOne({})
res.send(result);
} catch(error) {
res.send(error)
}
}
But when I try this like below, I am not sure it works asyncrously or not.
exports.query = async(req, res) => {
try{
user.findOne({})
.then(async(result) =>{
order.findOne({id: result.id})
.catch(e => res.send(e)
} catch(error) {
res.send(error)
}
}
I have to attach 'await' in front of user.findOne({}) like below?
exports.query = async(req, res) => {
try{
await user.findOne({})
.then(async(result) =>{
Or it doesn't matter? That is, it works same asycrously, even though I don't write await in front to user.findOne using 'then'?
thank you so much for reading.
Generally, inside a single block, you should either use await, or use .then, but not both - similarly, using Promise.prototype.catch is really weird in combination with try/catch where you can already await.
For your exports.query to resolve once the second findOne finishes, await or return both Promises, and connect the inner findOne connected to the outer Promise chain. Consider something like this instead:
exports.query = async (req, res) => {
try {
const userResult = await user.findOne({});
const orderResult = await userResult.findOne({ id: userResult.id });
// do something with orderResult
} catch (error) {
res.send(error)
}
}
If you use .then and catch without await, then it would look like:
exports.query = (req, res) => {
return user.findOne({})
.then((result) => {
return order.findOne({
id: result.id
})
.then((orderResult) => {
// do something with orderResult
})
.catch(e => res.send(e))
});
}
With the code in the second snippet in your question, your try/catch block will never do anything useful, because any problem will trigger the .catch method, and because the Promise isn't being awaited. Better not to mix the two styles - either pick await and try/catchor .then and .catch, but not both, else the control flow may become difficult to make sense of.
First of all, async-await can be used in functions which returns a promise.
Why we have to use await instead of then. await process the code asynchronously by making the execution feel like synchronous.
In your first example, everything works fine as expected. But in the second one, you need to await the findOne query. Here the fineOne will work asynchronously
exports.query = async(req, res) => {
try{
user.findOne({})
.then(async(result) =>{
let orderResult = await order.findOne({id: result.id})
.catch(e => res.send(e)
} catch(error) {
res.send(error)
}
}
which can be again simplified to
exports.query = async(req, res) => {
try{
let result = await user.findOne({});
let orderResult = order.findOne({id: result.id});
} catch(error) {
res.send(error)
}
}
I am trying to query my database several times and construct an object which stores every response from my database in a field. Here is my code:
router.post('/search', (req, res) => {
var collection = db.get().collection('styles')
var data = [];
collection.distinct('make.name', (err, docs) => {
data.push({'make': docs });
});
collection.distinct('model', (function (err, docs) {
data.push({'model': docs });
}))
res.send(data);
});
Since NodeJS/Express is asynchronous, this isn't working as I would like. How can I reconstruct this endpoint to make several database calls (from the same collection) and return an object containing it?
There's more than one way to do it:
Nested callbacks
Without promises you could nest the callbacks:
router.post('/search', (req, res) => {
var collection = db.get().collection('styles')
var data = [];
collection.distinct('make.name', (err, docs) => {
if (err) {
// ALWAYS HANDLE ERRORS!
}
data.push({'make': docs });
collection.distinct('model', (function (err, docs) {
if (err) {
// ALWAYS HANDLE ERRORS!
}
data.push({'model': docs });
res.send(data);
}))
});
});
This would be the easiest way, but note that it is not efficient if those two requests could be done in parallel.
The async module
You can use the async module:
router.post('/search', (req, res) => {
var collection = db.get().collection('styles')
var data = [];
async.parallel({
make: cb => collection.distinct('make.name', cb),
model: cb => collection.distinct('model', cb),
}, (err, responses) => {
if (err) {
// ALWAYS HANDLE ERRORS!
}
data.push({'make': responses.make });
data.push({'model': responses.model });
res.send(data);
});
});
See: https://caolan.github.io/async/docs.html#parallel
But this may still not be the most convenient method.
ES2017 async/await
The most flexible way of doing that if you have 30 calls to make would be to:
Use functions that return promises instead of functions that take callbacks
Use async/await if you can or at least generator based coroutines
Await on promises (or yield promises) when the logic needs to run in sequence
Use Promise.all() for anything that can be done in parallel
With async/await your code could look like this:
// in sequence:
var make = await collection.distinct('make.name');
var model = await collection.distinct('model');
// use 'make' and 'model'
Or:
// in parallel:
var array = await Promise.all([
collection.distinct('make.name'),
collection.distinct('model'),
]);
// use array[0] and array[1]
A big advantage of async/await is the error handling:
try {
var x = await asyncFunc1();
var array = await Promise.all([asyncFunc2(x), asyncFunc3(x)]);
var y = asyncFunc4(array);
console.log(await asyncFunc5(y));
} catch (err) {
// handle any error here
}
You can only use it inside of a function created with the async keyword. For more info, see:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
For support in browsers, see:
http://caniuse.com/async-functions
For support in Node, see:
http://node.green/#ES2017-features-async-functions
In places where you don't have native support for async and await you can use Babel:
https://babeljs.io/docs/plugins/transform-async-to-generator/
or with a slightly different syntax a generator based approach like in co or Bluebird coroutines:
https://www.npmjs.com/package/co
http://bluebirdjs.com/docs/api/promise.coroutine.html
See those answers for more info:
try/catch blocks with async/await
node.js ~ constructing chained sequence of Promise resolves
How to run Generator Functions in Parallel?
node.js ~ constructing chained sequence of Promise resolves
Using async/await + Bluebird to promisifyAll
jQuery: Return data after ajax call success
You can do it with Promises
router.post('/search', (req, res) => {
var collection = db.get().collection('styles');
// Create promise for "make.name" query
let firstQuery = new Promise((resolve, reject) => {
collection.distinct('make.name', (err, docs) => {
if (!err) {
resolve(docs);
} else {
reject(err);
}
});
});
// Create promise for "model" query
let secondQuery = new Promise((resolve, reject) => {
collection.distinct('model', (function (err, docs) {
if (!err) {
resolve(docs);
} else {
reject(err);
}
}))
})
// Run both queries at the same time and handle both resolve results or first reject
Promise.all([firstQuery, secondQuery])
.then((results) => {
res.send({ "make.name": results[0], "model": results[1] });
})
.catch((err) => {
// Catch error
res.send({});
});
});
Also you can use destructuring in callback functions like that:
Promise.all([firstQuery, secondQuery])
.then(([makeName, model]) => res.send({ "make.name": makeName, model }))
UPD: If you have a bunch of collection to request you can create an array of collections name, map it to promise requests and handle with Promise.all, for example
let collections = ["firstCollection", "secondCollection", "nCollection"];
let promises = collections.map((collectionName) => {
return new Promise((resolve, reject) => {
collection.distinct(collectionName, (err, docs) => {
if (!err) {
resolve(docs)
} else {
reject(err);
}
});
})
});
Promise.all(promises)
.then(results => {
// Do what you want to do
})
.catch(error => {
// or catch
});