I am working on a Discord bot and need to return a value after the reaction promise finishes.
function sendDM(userTag) {
let sentMessage = undefined;
let moderator = undefined;
let status = "";
try {
client.users.fetch(userTag, false).then((user) => {
user.send("User Authentication Request -\n```Discord Tag:" + userTag + "\n" + "Date Sent: " + getDate() + "\n" + "```" + "Authorize?").then((message => {
moderator = user
sentMessage = message
message.react("✅");
message.react("❌");
}));
})
client.on("messageReactionAdd", (reaction, user) => {
if (reaction.emoji.name == "✅") {
const authorTag = client.users.cache.find(u => u.tag === moderator.tag);
if (user.id == authorTag) {
status = "complete";
console.log("Auth Request Complete");
moderator.send("Authentication request was successful.");
}
} else {
if (reaction.emoji.name == "❌") {
const authorTag = client.users.cache.find(u => u.tag === moderator.tag);
if (user.id == authorTag) {
status = "dropped";
console.log("Auth Request Dropped: User Denied");
moderator.send("Authentication request was dropped.");
}
}
}
}).then(someShit => console.log("test: " + someShit))
} catch(error) {
console.log("DM ERROR_" + error);
}
// Return would be here
}
The problem is once the by the time the user reacts, the function has already returned the empty "status" string.
I'm not an expert in the field, but it looks like you could wrap your code block in a Promise and wait until the promise has been resolved by your code. Something like this might work:
async function sendDM(userTag) {
let sentMessage = undefined;
let moderator = undefined;
let status = "";
const statusPromise = new Promise((resolve, reject) => {
try {
client.users.fetch(userTag, false).then((user) => {
user
.send(
"User Authentication Request -\n```Discord Tag:" +
userTag +
"\n" +
"Date Sent: " +
getDate() +
"\n" +
"```" +
"Authorize?"
)
.then((message) => {
moderator = user;
sentMessage = message;
message.react("✅");
message.react("❌");
});
});
client
.on("messageReactionAdd", (reaction, user) => {
if (reaction.emoji.name == "✅") {
const authorTag = client.users.cache.find(
(u) => u.tag === moderator.tag
);
if (user.id == authorTag) {
resolve("complete");
console.log("Auth Request Complete");
moderator.send("Authentication request was successful.");
}
} else {
if (reaction.emoji.name == "❌") {
const authorTag = client.users.cache.find(
(u) => u.tag === moderator.tag
);
if (user.id == authorTag) {
resolve("dropped");
console.log("Auth Request Dropped: User Denied");
moderator.send("Authentication request was dropped.");
}
}
}
})
.then((someShit) => console.log("test: " + someShit));
} catch (error) {
console.log("DM ERROR_" + error);
reject(error);
}
});
status = await statusPromise;
// Return would be here
}
This might introduce problems elsewhere in your code since you have to make your function async to use the await keyword, but this way your code should wait until a value has been returned from the promise.
Use async/await to halt the execution of the function until the promise has resolved:
async function sendDM(userTag) {
...
await client.on("messageReactionAdd", (reaction, user) => {
...
// Promise has resolved so the data is available
}
Related
I am trying to write a Javascript promise that resolves and rejects the desired variables. I am getting the error message below after running in the console
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().
code: 'ERR_UNHANDLED_REJECTION'
Here is my code
Js File:
const grimeUserLeft = false;
const userWatchingGrimeVids = true;
let grimePromise = new Promise((resolve, reject) => {
if (grimeUserLeft) {
reject("User Left");
} else if (userWatchingGrimeVids) {
reject("This user is a G");
} else {
resolve("Congrats. Your channel doesn't suck");
}
});
grimePromise.then((message) => {
console.log("Success: " + message);
});
grimePromise.catch((message) => {
console.log("You Failed: " + message);
});
By calling the .then() and the .catch() handler on two different places, you actually end up with two different promises - each of .then() and .catch() returns a new one. The promise returned by .then() will not be caught by the separate .catch() handler:
const grimeUserLeft = false;
const userWatchingGrimeVids = true;
let grimePromise = new Promise((resolve, reject) => {
if (grimeUserLeft) {
reject("User Left");
} else if (userWatchingGrimeVids) {
reject("This user is a G");
} else {
resolve("Congrats. Your channel doesn't suck");
}
});
grimePromise
.then((message) => {
console.log("Success: " + message);
})
.catch(() => console.log(1));
grimePromise.catch((message) => {
console.log(2);
console.log("You Failed: " + message);
});
Therefore, one promise gets turned into two. Each of them will adopt the state of the original promise. When the original is rejected, both the derived promises reject. Yet only one is handled.
Instead, you can directly specify the .catch() in the same promise chain as the .then():
const grimeUserLeft = false;
const userWatchingGrimeVids = true;
let grimePromise = new Promise((resolve, reject) => {
if (grimeUserLeft) {
reject("User Left");
} else if (userWatchingGrimeVids) {
reject("This user is a G");
} else {
resolve("Congrats. Your channel doesn't suck");
}
});
grimePromise
.then((message) => {
console.log("Success: " + message);
})
.catch((message) => {
console.log("You Failed: " + message);
});
<h1>Check the browser console - no unhandled promise rejections</h1>
Or alternatively, specify both handlers in the then():
const grimeUserLeft = false;
const userWatchingGrimeVids = true;
let grimePromise = new Promise((resolve, reject) => {
if (grimeUserLeft) {
reject("User Left");
} else if (userWatchingGrimeVids) {
reject("This user is a G");
} else {
resolve("Congrats. Your channel doesn't suck");
}
});
grimePromise
.then(
(message) => {
console.log("Success: " + message);
},
(message) => {
console.log("You Failed: " + message);
}
);
<h1>Check the browser console - no unhandled promise rejections</h1>
The reason this isn't working is because you didn't have a .catch() on the first one. It was throwing a rejection, but you weren't handling it gracefully with a catch, so it threw.
Here is your code working - I just moved your catch block to go after the .then:
const grimeUserLeft = false;
const userWatchingGrimeVids = true;
const grimePromise = new Promise((resolve, reject) => {
if (grimeUserLeft) {
reject('User Left');
} else if (userWatchingGrimeVids) {
reject('This user is a G');
} else {
resolve("Congrats. Your channel doesn't suck");
}
});
grimePromise
.then((message) => {
console.log('Success: ' + message);
})
.catch((message) => {
console.log('You Failed: ' + message);
});
Just as a tip though, you should only reject whenever the function is unsuccessful in some way. That could be a timeout, a network error, incorrect input etc. Whatever is inside of a reject should ideally be an error.
Additionally, I'd recommend using async await, as it's much easier to read and more manageable. You can easily do this by creating a function which returns your new Promise, then awaiting it like so:
const grimeUserLeft = false;
const userWatchingGrimeVids = true;
const grime = () => {
return new Promise((resolve, reject) => {
if (grimeUserLeft) {
reject(new Error('User Left'));
} else if (userWatchingGrimeVids) {
resolve('This user is a G');
} else {
resolve("Congrats. Your channel doesn't suck");
}
});
};
const doStuff = async () => {
try {
const result = await grime();
console.log(result);
} catch (error) {
console.error(error);
}
};
doStuff();
To make this truly asynchronous, you should ideally be doing some async stuff inside of the promise. This can be demonstrated with a setTimeout:
const grimeUserLeft = false;
const userWatchingGrimeVids = true;
const grime = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (grimeUserLeft) {
reject(new Error('User Left'));
} else if (userWatchingGrimeVids) {
resolve('This user is a G');
} else {
resolve("Congrats. Your channel doesn't suck");
}
}, 2000);
});
};
const doStuff = async () => {
try {
const result = await grime();
console.log('result finished after 2 seconds');
console.log(result);
} catch (error) {
console.error(error);
}
};
doStuff();
I am trying to create a user with email and password using firebase, but when I call the function that creates it, it is called twice and I get an error because I am trying to register the email that is already in use.
I noticed that the console.log('CALLED') is called once, I don't understand why RegisterWithEmail is called twice. My auth flow only creates the userDocument in the confirmation phase, for this reason userSnap.length equals zero in the second call and tries to create again.
How can I call this function once?
FILE: emailconfirm.page.tsx
registerEmail = async data => {
const { setRegStatus, createDoc } = this.props;
console.log('CALLED')
await RegisterWithEmail(data).then(res => {
console.log('Final response ', res)
if(res === 'EMAIL_VERIFIED') {
createDoc()
setRegStatus({ status: 'created', data: res })
}
else if(res === 'SOMETHING_WENT_WRONG'){
setRegStatus({ status: 'error', data: res })
}
}).catch(err => {
console.log('Error ', err)
setRegStatus({ status: 'error', data: err })
})
}
FILE: firebase.utils.tsx
export const RegisterWithEmail = async user => {
console.log("Called Once...");
if(!user) return 'SOMETHING_WENT_WRONG';
else {
const snap = await firestore.collection('users').where('email', '==', user.email).get();
const docs = snap.docs.map((doc) => doc.data());
if (docs.length !== 0) return 'EMAIL_HAS_ALREADY_BEEN_TAKEN';
try {
console.log("Trying to register email...");
return await auth.createUserWithEmailAndPassword(user.email, user.password).then(async usr => {
await usr.user.updateProfile({
displayName: user.name
}) // SETTING NAME
const sendVerifyEmail = usr.user.sendEmailVerification().then(() => setTimer(usr.user, 5))
return await sendVerifyEmail.then(msg => {
console.log('Finishing...', msg)
if(msg.txt !== 'waiting') {
if(msg.error) {
throw msg.txt
}
else return msg.txt
}
}).catch(() => {
throw 'EMAIL_NOT_SENT'
})
}).catch(() => {
throw 'USER_NOT_CREATED'
})
} catch (err) {
throw 'USER_ALREADY_REGISTERED'
}
}
}
Developer console:
You shouldn't be mixing and matching .then()s in async functions for your own sanity's sake.
Something like
export const RegisterWithEmail = async (user) => {
if (!user) return false;
const snap = await firestore.collection("users").where("email", "==", user.email).get();
const docs = snap.docs.map((doc) => doc.data());
if (docs.length !== 0) return false;
console.log("Trying to register email...");
try {
const resp = await auth.createUserWithEmailAndPassword(user.email, user.password);
// then ...
return true;
} catch (err) {
// catch ...
}
};
might work better for you.
I need more code to be sure, but I think you should add await
registerEmail = async data => {
console.log('CALLED')
await RegisterWithEmail(data)
}
I want to get the result of an async function - depending on the result I either want to execute another function or continue the for loop. Here is my Cloud Function:
return query.get().then(geoSnapshot => {
// If GeoquerySnapshot returns 0 docs, createDoc()
let docs = geoSnapshot.docs;
if (docs.length === 0) return createDoc(data);
for(let [index, doc] of docs.entries()){
// If age preferences are valid, joinDoc
let currentDoc = docs[index].data();
if (validAge(currentDoc, data)){
let matchedBefore = await(matchedBefore(currentDoc, data))
if (!matchedBefore){
return joinDoc(docs[index], data);
} else if (index === (docs.length - 1)) {
return createDoc(data);
}
}
return
}
}).catch( error => {
console.log("error query: " + error);
return { error: error };
})
async function matchedBefore(currentDoc, data){
return db.collection('users').doc(data.uid).get().then( doc => {
if ( !doc.exists ) return false;
// If user1 in matchedUsers
let matchedUsers = doc.get('matchedUsers');
if (matchedUsers === undefined) return true
let matchedBefore = matchedUsers.includes(currentDoc.user1);
console.log('matchedBefore: ' + matchedBefore);
if (matchedBefore) {
return false
} else {
return true
}
})
}
I'm getting this following error on let matchedBefore = await(matchedBefore(currentDoc, data)):
Each then() should return a value or throw
How can I ensure the function matchedBefore() finishes before the for loop continues?
I think you are too confused with the implementation of async-await. The correct way would be:
return query.get().then(async geoSnapshot => {
// If GeoquerySnapshot returns 0 docs, createDoc()
let docs = geoSnapshot.docs;
if (docs.length === 0) return createDoc(data);
/* eslint-disable no-await-in-loop */
for(let [index, doc] of docs.entries()) {
// If age preferences are valid, joinDoc
let currentDoc = docs[index].data();
if (validAge(currentDoc, data)){
let matchedBefore = await(matchedBefore(currentDoc, data))
if (!matchedBefore){
return joinDoc(docs[index], data);
} else if (index === (docs.length - 1)) {
return createDoc(data);
}
}
return
}
/* eslint-enable no-await-in-loop */
}).catch( error => {
console.log("error query: " + error);
return { error: error };
})
async function matchedBefore(currentDoc, data){
let doc = await db.collection('users').doc(data.uid).get()
if ( !doc.exists ) return false;
// If user1 in matchedUsers
let matchedUsers = doc.get('matchedUsers');
if (matchedUsers === undefined) return true
let matchedBefore = matchedUsers.includes(currentDoc.user1);
console.log('matchedBefore: ' + matchedBefore);
return !matchedBefore
}
I am new to node.js.I am using await/async to avoid function to run before completing previous one.First of all here is code.
---------------first-----------
exports.createProfileUser = async (req, res, next) => {
const {user_image,image_two,
image_three,image_four,
image_five} = req.files
try{
var filetype = req.files.user_image.name.split(".");
var newFileName = "./public/images/"+Date.now()
+ Math.floor(100000 + Math.random() * 900000)
+ "."+filetype[1]
await insertImages(req,newFileName)
await createProfile(req,res,newFileName.substring(1), (result) =>{
if(result){
return res.json(result)
}
else{
return res.json({
status:false,
message:'Error Occurred '
})
}
})
. then((result) => {
if(result){
return res.json(result)
}
else{
return res.json({
status:false,
message:'Error Occurred '
})
}
})
}
catch(error){
res.sendStatus(500) && next(error)
}};
In above code i want insertImages function to be fully finished before calling createProfile.But that's not happening.Here is insertImages code.
--------------two--------------
insertImages = (req,baseFileName) =>{
const {user_id} = req.fields
var newFileNameTemp = baseFileName.substr(1);
if(image){
let fileType = req.files.image_five.name.split(".")
let name = "./public/images/"+Date.now() + "." + fileType[1]
let tempName = name.substr(1);
fs.copyFile(req.files.image_five.path,
name,
async(err) => {
if (err) {
console.log("Error in copying file",err)
return res.json({
status:false,
message:"Error occurred while updating Profile Pic Two of the user ",
})
}
await makeGallery(user_id,tempName, (resultId)=>{
console.log('resultId,',resultId)
})
});
}
}
After copying file i am saving a object in gallery schema and after that get its _id and update that to users schema.Code for that is.
--------------threee----------------
makeGallery = async (user_id,imageName,callback) => {
var g = new Gallery();
g.image = imageName
var saved = await g.save()
.then( async (gallery) => {
await Users.findOneAndUpdate({user_id: user_id},{$push: {gallery:[gallery._id]}},
{ upsert: true }
)
.then(async(data) =>{
await console.log("Successfully added");
})
await callback(gallery._id)
})
.catch(err => {
return {
status:false,
message:err.message,
}
});
}
I have used await so function will wait till data updates. But that's not working what i expected.createProfile function is called before insertImages is finished.What i am doing wrong here?Please correct my code i am stuck in this issue.Thanks in advance.
Mainly you are not synchronizing with fs.copyFile which is asynchronous
>>> insertImages = async (req,baseFileName) => {
const {user_id} = req.fields
var newFileNameTemp = baseFileName.substr(1);
if(image){
let fileType = req.files.image_five.name.split(".")
let name = "./public/images/"+Date.now() + "." + fileType[1]
let tempName = name.substr(1);
>>> await new Promise(function(resolve, reject){
>>> return fs.copyFile(req.files.image_five.path, name, err => {
if (err) {
console.log("Error in copying file",err)
// you should return the response server elsewhere. NOT in insertImages..
// you should throw a proper error as well
>>> return reject(res.json({
status:false, // should be 500 for internal server error
message:"Error occurred while updating Profile Pic Two of the user ",
}))
}
>>> return resolve()
})
})
}
await makeGallery(user_id,tempName, (resultId)=>{
console.log('resultId,',resultId)
})
}
Eventually, you may use
const copyFile = require('util').promisify(fs.copyFile)
so you can directly write
>>> insertImages = async (req,baseFileName) => {
const {user_id} = req.fields
var newFileNameTemp = baseFileName.substr(1);
if(image){
let fileType = req.files.image_five.name.split(".")
let name = "./public/images/"+Date.now() + "." + fileType[1]
let tempName = name.substr(1);
>>> await copyFile(req.files.image_five.path, name).catch(err => {
console.log("Error in copying file",err)
>>> throw res.json({
status:false,
message:"Error occurred while updating Profile Pic Two of the user ",
})
})
}
await makeGallery(user_id,tempName, (resultId)=>{
console.log('resultId,',resultId)
})
}
I have a user list and i am checking all user with certain details.I am using sequelize js with express.I want to know that can we use while loop like this for searching and saving data in database.Please help me.Thanks In advance.
let royalty_bonus = async (sponsor) => {
return await new Promise((resolve, reject) => {
models.RoyaltyUser.findById(sponsor)
.then(async (sponsorRow) => {
let user_level = 1;
let sponsor_id = sponsorRow;
try {
while (sponsor_id != null && user_level <= 3) {
let level_length = await getLevel(sponsor_id.id, user_level);
if (user_level === 1 && level_length.length === 3) {
console.log('Level One Achieved By ', sponsor_id.id);
} else if (user_level === 2 && level_length.length === 9) {
console.log('Level Two Is Achieved By ', sponsor_id.id);
} else {
console.log('No Level');
}
await models.RoyaltyUser.findOne({where: {id: sponsor_id.sId}})
.then((sponsor_new_row) => {
sponsor_id = sponsor_new_row;
})
.catch((e) => {
console.log(' Inner Catch Error ', e.message);
reject();
});
user_level++;
}
resolve();
}
catch (e) {
reject(e);
}
})
.catch((e) => {
reject('catch ', e.message);
});
});
};
router.get('/royalty_user', async (req, res, next) => {
royalty_bonus(4)
.then(() => {
console.log('done');
})
.catch((e) => {
console.log('Catch two', e.message);
})
});
Avoid the Promise constructor antipattern, avoid return await, and don't mix .then callbacks with async/await syntax. You can simplify a lot:
async function royalty_bonus(sponsor) {
const sponsorRow = await models.RoyaltyUser.findById(sponsor);
let user_level = 1;
let sponsor_id = sponsorRow;
while (sponsor_id != null && user_level <= 3) {
let level_length = await getLevel(sponsor_id.id, user_level);
if (user_level === 1 && level_length.length === 3) {
console.log('Level One Achieved By ', sponsor_id.id);
} else if (user_level === 2 && level_length.length === 9) {
console.log('Level Two Is Achieved By ', sponsor_id.id);
} else {
console.log('No Level');
}
const sponsor_new_row = await models.RoyaltyUser.findOne({where: {id: sponsor_id.sId}});
sponsor_id = sponsor_new_row;
user_level++;
}
}
router.get('/royalty_user', (req, res, next) => {
royalty_bonus(4).then(() => {
console.log('done');
}, e => {
console.log('Catch two', e.message);
});
});