So, I'm new trying to understand how async functions work. Doing it with "Resolve, Reject" from Promises, works fine. But when I try to apply it with async instead of new Promise, for some reason, the function returns undefined. Here is my code :)
note: Sorry for my english, not fluent speaker :)
category = body.category
obtenerCategoria = async(category) => {
Categoria.findOne({ descripcion: category })
.exec((err, categoriaDB) => {
if (err) {
throw new Error(err)
}
if (!categoriaDB) {
return res.status(400).json({
ok: false,
msg: 'Categoria no encontrada'
})
}
console.log(categoriaDB); // Works fine here
return categoriaDB
})
}
crearClase = async() => {
categoria = await obtenerCategoria(category);
console.log(categoria); //getting nothing here
}
crearClase()
.then()
.catch(e => {
return e
})
You don't need to use callback function when you use async/await
Try this code:
obtenerCategoria = async(category) => {
const categoriaDB = await Categoria.findOne({ descripcion: category });
if (!categoriaDB) {
return res.status(400).json({
ok: false,
msg: 'Categoria no encontrada'
})
}
console.log(categoriaDB); // Works fine here
return categoriaDB
}
obtenerCategoria doesn't have a return value. Additionally, you can use await on the Categoria.findOne() call and use try / catch instead of the call to exec(), something like this example.
Related
I connect to a Postgres database in Node and I get a return value from the database. However, when I try to pass that value to another function, the value is undefined. Check out this code:
async function doDbCall(queryString) {
await client.connect()
.then(async () => {
await client.query(queryString)
.then(dbRes => {
console.log("dbDoCall - dbRes", dbRes.rows[0]);
return dbRes;
})
});
}
Here is the calling function:
async function testTheDb() {
try {
const myReturnVal = await dbConn.doDbCall("SELECT NOW() as now");
console.log("db val:", myReturnVal);
}
catch(ex) {
console.log("db err", ex.message);
}
}
In the console.log in doDbCall() on the query.then success, I get a value in dbRes.rows[0]. The console.log shows dbDoCall - dbRes { now: 2022-09-26T16:47:14.465Z }. So I return that value.
However, in the testTheDb() function, I have another console log where I log the value of myReturnVal. The result of that log shows db val: undefined.
My question is this: Why is myReturnVal null in testTheDb()?
You should not mix async/await syntax with .then() calls. Your problem is that neither the doDbCall function nor the async () => {…} callback do return anything, your only return keyword is nested inside a nested then callback.
You should write either
function doDbCall(queryString) {
return client.connect()
.then(() => {
return client.query(queryString);
})
.then(dbRes => {
console.log("dbDoCall - dbRes", dbRes.rows[0]);
return dbRes;
})
.finally(() => {
client.end();
});
}
or
async function doDbCall(queryString) {
try {
await client.connect()
const dbRes = await client.query(queryString);
console.log("dbDoCall - dbRes", dbRes.rows[0]);
return dbRes;
} finally {
client.end();
}
}
I've been trying to create a helper function to return a document's data from the Firebase database using Nodejs:
module.exports = async (collectionName, documentId, res) => {
const collection = db.doc(`/${collectionName}/${documentId}`);
try {
const targetedDocument = await collection.get();
if (!targetedDocument.exists) {
return res.status(404).json({
error: true,
message: `the document ${documentId} is not exists.`,
});
}
return targetedDocument.data();
} catch (error) {
return res.status(500).json({ error: true, message: error });
}
};
But when I tried to use it, it always returns back a promise:
const documentFinder = require('./helper-function-path');
router.post('post',(req,res)=>{
const requiredDocument = documentFinder("post", "Izkn12IMnayzokLqe",res);
console.log(requiredDocument); //returned a promise rather than an object document
})
What am I doing wrong here? Some pointer would be very much appreciated. Thank you.
async functions, by definition, always return a promise. You can't make an asynchronous function to be synchronous simply by wrapping it. The caller will always still have the promise to deal with. You can deal with the returned promise in your express route by making its callback also async, and awaiting the result of the function call:
router.post('post', async (req,res)=>{
const requiredDocument = await documentFinder("post", "Izkn12IMnayzokLqe",res);
console.log(requiredDocument);
})
Please try out:
module.exports = async (collectionName, documentId, res) => {
const collection = db.doc(`/${collectionName}/${documentId}`);
try {
const targetedDocument = await collection.get();
if (!targetedDocument.exists) {
return res.status(404).json({
error: true,
message: `the document ${documentId} is not exists.`,
});
} else {
return targetedDocument.data();
}
} catch (error) {
return res.status(500).json({ error: true, message: error });
}
};
As firebase functions now run w Node8, I would like to transform my current ES5 function w Promise flow to ES6 async/await
my flow pattern is the following :
const AUTHORIZED = authorizedApi()
if AUTHORIZED
const SENT = sendContactMessage())
if SENT
const FOUND = findContact(
if FOUND
return "FINISHED"
if !FOUND
const CREATED = createContact()
if CREATED
return "FINISHED"
Currently I am using a specific conditionalPromiseFlow() function as following : ( need to handle also the errors..
const conditionalPromiseFlow = (...fns) => {
if (fns.length === 0) return Promise.resolve();
const [next] = fns;
return next().then(result => {
if (result) {
return conditionalPromiseFlow(...fns.slice(1));
}
return result;
});
};
and I call it :
conditionalPromiseFlow(
() => authorizedApi(jwtClient),
() => sendContactMessage(gmailAPI, encodedContactMessage),
() =>
findContact(
googlePeopleAPI.connections,
googleConnectionListParams,
sender.email
),
() => createContact(googlePeopleAPI, googlePeopleContactParams)
)
.then(
res => {
return { status: 200, infos: "done" };
},
error => {
return { status: error.status, infos: error.message };
}
)
.then(response => {
return res.send(response);
})
.catch(console.error);
this runs well, but I guess that async/await pattern would simplify my code... Is it true or should I stick to my current code ?
thanks for feedback
Assuming this is not contained in an async function, the async/await equivalent would be:
(async() => {
try {
await authorizedApi(jwtClient);
await sendContactMessage(gmailAPI, encodedContactMessage);
await findContact(
googlePeopleAPI.connections,
googleConnectionListParams,
sender.email
);
await createContact(googlePeopleAPI, googlePeopleContactParams);
res.send({ status: 200, infos: "done" });
} catch (error) {
res.send({ status: error.status, infos: error.message });
}
))();
Whether that's simpler and a change worth making is obviously up to you.
(From your code, I take it when the promises returned by those functions reject, the object they provide has a status on it.)
Note that I didn't put a try/catch around the last res.send. I don't think it throws, but you did have a catch handler on it. So if it throws, you'd want to put that back.
If you're already in an async function, obviously you don't need that async wrapper:
try {
await authorizedApi(jwtClient);
await sendContactMessage(gmailAPI, encodedContactMessage);
await findContact(
googlePeopleAPI.connections,
googleConnectionListParams,
sender.email
);
await createContact(googlePeopleAPI, googlePeopleContactParams);
res.send({ status: 200, infos: "done" });
} catch (error) {
res.send({ status: error.status, infos: error.message });
}
By res.send it seems like You're using express framework - so You can make handler to be async wrapper, it's enough to put async word before (req, res):
app.get('/something', async (req, res) => {
try {
/*
await stuff here
*/
res.send({ status: 200, infos: "done" });
} catch (error) {
res.send({ status: error.status, infos: error.message });
}
});
Note that both in the above and in the async wrapper in the first code block that the entire body is in the try (other than the res.send on error). That's because nothing will handle the promise from the async function (Express doesn't do anything with the return value of route callbacks), so it's important that promise doesn't reject.
I'm struggling to understand Promise but it's really difficult for me.
it makes me crazy, I would appreciate your helps.
this is a code and I want "console.log(data)" after Recommend function finished
but the result is undefined.
What should I do?
many thanks
This is app.js
var _promise = function () {
return new Promise((resolve,reject) => {
var data = getJS.Recommend(req.query.User_id)
resolve(data)
}).then(function(data) {
console.log(data)
})
_promise();
}})
this is RecommendPost.js
exports.Recommend =(myId)=>{
var posts=[]
User.find({
User_id : myId
}).then((result)=>{
return User.find()
.select('User_id')
.where('age').equals(result[0].age)
}).then((User_id)=>{
return Promise.all(User_id.map((user,idx,arr)=>{
return Count.find()
.select('board_id')
.where('User_id').equals(user.User_id)
}))
}).then((Users_id)=>{
Users_id.forEach(items=>{
items.forEach(post=>{
posts.push(post.board_id)
})
})
}).then(()=>{
return getMax(posts);
})
}
cf. In RecommendPost.js, the posts works synchronously
//-------- I solved this problem! as some guys said, Recommend function should return Promise. So I edited, then this worked !
this is edited code. thank you for helping me :)
This is app.js
var _promise = function () {
return new Promise((resolve, reject) => {
getJS.Recommend(req.query.User_id).then((data) => {
resolve(data);
})
})
}
_promise().then((data) => { console.log(data) });
this is RecommendPost.js
exports.Recommend =(myId)=>{
return new Promise((resolve,reject)=>{
var posts=[]
User.find({
User_id : myId
}).then((result)=>{
return User.find()
.select('User_id')
.where('age').equals(result[0].age)
}).then((User_id)=>{
return Promise.all(User_id.map((user,idx,arr)=>{
return Count.find()
.select('board_id')
.where('User_id').equals(user.User_id)
}))
}).then((Users_id)=>{
Users_id.forEach(items=>{
items.forEach(post=>{
posts.push(post.board_id)
})
})
}).then(()=>{
resolve (getMax(posts));
})
})
}
It might be clearer for you with async/await that makes asynchronous code look like synchronous:
async function main() {
var data = await getJS.Recommend(req.query.User_id); // waits till its done before going to the next line
console.log(data);
}
// unimportant specific implementation details for live example:
const getJS = {
Recommend(id) {
data = "some data for user ";
return new Promise((res) => setTimeout(() => res(data + id), 1000));
}
}
const req = {
query: {
User_id: 5
}
}
main();
When you call to resolve function is mean that the function is finish and it returns to the caller.
the data object in the then doesn't exist
The Promise.resolve(value) method returns a Promise object that is
resolved with the given value. If the value is a promise, that promise
is returned; if the value is a thenable (i.e. has a "then" method),
the returned promise will "follow" that thenable, adopting its
eventual state; otherwise the returned promise will be fulfilled with
the value.
ES5 :
var _promise = function (req) {
return new Promise((resolve,reject) => {
getJS.Recommend(req.query.User_id).then((data)=>{
console.log(data)
resolve(data)
},(err)=>{
console.log(err)
reject(err);
})
})
}
_promise(req);
ES6
async function _promise(req) {
return await getJS.Recommend(req.query.User_id)
}
For your edit(The entire code):
In this case that Recommend function returns a promise, you don't need the _promise function that wraps it with another one.
and you call it direct
getJS.Recommend(req.query.User_id).then(
(data) => {
console.log(data)
});
Promises means it will always return something, either error or success.
if you resolve something then it will return from there. so your .then part will not execute.
so if you are using resolve and reject params in function then, use .catch method.
return new Promise((resolve,reject) => {
var data = getJS.Recommend(req.query.User_id)
resolve(data)
}) .catch(function (ex) { // in case of error
console.log(ex);
})
i'm having this error and haven't got to resolve it though have researched a lot in MDN and here. As title saysinto VUE i'm trying to use async and await but js is not waiting the 'await' function to end. Here it is:
methods: {
async search (terms, done) {
console.log('1.')
this.filter = this.$refs.chipsInput.input
await this.loadtags()
console.log('3.')
done(this.tagsList)
},
loadtags () {
this.$axios
.get('/api/tags/?id__icontains=&id=&name__icontains=' + this.filter + '&name=&ordering=name&page_size=20')
.then(response => {
console.log('2.', response.data.results)
let temp = response.data.results
this.tagsList = temp.map(obj => {
return {
name: obj.name,
label: obj.name,
value: obj.name,
idField: obj.id
}
})
})
},
I am not able to post pictures yet, but add a link where you can look the console log where js prints the '3.' (which is placed after the await call) before '2.':
Image:
console
¿What am i doing wrong? already tried modifying the await like this:
let foo = await this.loadtags() and including a 'return 0' at the end of loadtags function but didn't work for me. Probably is a dumb thing, excuse me for that.
You aren't returning anything from the loadtags method, so the code doesn't wait.
Change this:
loadtags () {
this.$axios
.get(...
To this:
loadtags () {
return this.$axios
.get(...
async/await is more or less just sugar over Promises, so returning the Promise gives you something to await in the other method.
This is how I resolved this in my Vue application.
Before a user submits a new "tag" with submitNewTag() I need to check if it exists already in the list of tags, using async theTagExists().
submitNewTag() {
this.clearError();
this.theTagExists().then((res) => {
if (!res) {
console.log("TAG DOES NOT EXIST, SO ADD IT TO THE DATABASE");
this.saveTagToDatabase();
}
});
},
async theTagExists() {
console.log("CHECKING IF A TAG EXISTS");
await axios.get(`${this.apiUrl}/alltags`).then((res) => {
console.log("CHECKING IS DONE");
this.tagExists = res.data.allTags.some(
res =>
res.name.trim().toLowerCase() ===
this.newTag.tagName.trim().toLowerCase()
);
});
console.log("RETURN THE RESULT");
return this.tagExists;
},