JavaScript async/await not working as expected [duplicate] - javascript

This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 1 year ago.
I am using async/await but it is not working as expected. The data is being updated before the promise is resolved.
export const update = async (req, res) => {
let lead = Lead.findbyid(id)
let updates = req.body.updates
if (updates.clientDetails.alternateContacts && updates.clientDetails.alternateContacts.length > 0) {
updates.clientDetails.alternateContacts.forEach(async ele => {
if (ele.addedOn) console.log('date exists')
else ele.addedOn = new Date()
if (ele.email) {
console.log('11111111111111111111');
let clientId = await createAutoAccount({ email: ele.email, name:ele.name, contactNumber: ele.phoneNumber })
console.log('22222222222222222222222', clientId);
ele.userId = clientId
}
})
lead.clientDetails.alternateContacts = updates.clientDetails.alternateContacts
}
await lead.save();
}
export const createAutoAccount = async (data) => {
let user = await User.findOne({ email: data.email });
if (user) {
console.log('user found =======================', user._id);
} else {
console.log('new user created ---------------');
const tempObj = {
email: data.email,
password: data.password ? data.password : 'user123',
full_name: data.name ? data.name : null,
contact_number: data.contactNumber && !isNaN(data.contactNumber) ? data.contactNumber : null,
};
user = await User.create(tempObj);
}
return user._id;
}
The order of output is:
11111111111111111111
update lead
user found ======================= 617155c8827ab456041342ad
22222222222222222222222 617155c8827ab456041342ad
what is want is:
11111111111111111111
user found ======================= 617155c8827ab456041342ad
22222222222222222222222 617155c8827ab456041342ad
update lead
Please let me know what needs to be modified to make this work as per expectations.

I couldn't find where does the update lead log came from. But I think it should be coming from the lead.save(). If it is, the problem should be the async callback of the forEach loop.
updates.clientDetails.alternateContacts.forEach(async ele => { ... })
When you using forEach method, it accept a callback. The await in the for each method, which is the following apply only the that callback
if (ele.addedOn) console.log('date exists')
else ele.addedOn = new Date()
if (ele.email) {
console.log('11111111111111111111');
let clientId = await createAutoAccount({ email: ele.email, name:ele.name, contactNumber: ele.phoneNumber })
console.log('22222222222222222222222', clientId);
ele.userId = clientId
}
})
lead.clientDetails.alternateContacts = updates.clientDetails.alternateContacts
Try to make the await execution context to update function instead of the callback of the forEach by changing to the following code.
export const update = async (req, res) => {
let lead = Lead.findbyid(id)
let updates = req.body.updates
if (updates.clientDetails.alternateContacts && updates.clientDetails.alternateContacts.length > 0) {
// Use normal for loop instead of forEach with callback, the execution context will be the update function
for(const alternateContracts of updates.clientDetails.alternateContacts) {
if (ele.addedOn) console.log('date exists')
else ele.addedOn = new Date()
if (ele.email) {
console.log('11111111111111111111');
let clientId = await createAutoAccount({ email: ele.email, name:ele.name, contactNumber: ele.phoneNumber })
console.log('22222222222222222222222', clientId);
ele.userId = clientId
}
})
lead.clientDetails.alternateContacts = updates.clientDetails.alternateContacts
}
await lead.save();
}

I don't have enough rep to comment, so this should probably be a comment, but look at this post.
Using async/await with a forEach loop
From what I know your issue is due to you trying to use async await within a foreach loop. I have run into issues with that in the past.

Related

Nestjs Testing in signup BadRequestException: email in use error

user.service.ts
async findWithMail(email:string):Promise<any> {
return this.userRepository.findOne({email});
}
auth.service.ts
async signup(email:string,password:string,name?:string,surname?:string,phone:string){
if(email) {
const users = await this.userService.findWithMail(email);
if(users) {
throw new BadRequestException('email in use');
}
}
if(!password) return {error:"password must be!"};
const salt = randomBytes(8).toString('hex');
const hash = (await scrypt(password,salt,32)) as Buffer;
const result = salt + '.' +hash.toString('hex');
password = result;
const user = await
this.userService.create(email,password,name,surname,phone);
return user;
}
auth.service.spec.ts
let service:AuthService;
let fakeUsersService: Partial<UserService>;
describe('Auth Service',()=>{
beforeEach(async() => {
fakeUsersService = {
findWithMail:() => Promise.resolve([]),
create:(email:string,password:string) => Promise.resolve({email,password} as User),
}
const module = await Test.createTestingModule({
providers:[AuthService,{
provide:UserService,
useValue:fakeUsersService
}],
}).compile();
service = module.get(AuthService);
});
it('can create an instance of auth service',async()=> {
expect(service).toBeDefined();
})
it('throws an error if user signs up with email that is in use', async () => {
await service.signup('asdf#asdf.com', 'asdf')
});
})
When ı try to run my test its give me error even this email is not in database its give error: BadRequestException: email in use. I couldnt figure out how to solve problem
You can use isExists method instead of findOne.
Also you can add extra check for your findWithMail method. Check the length of db request result. Like if (dbReqResult.length === 0) return false; else true
please put your attention on your mocked user service, especially on findWithEmail function, this part
beforeEach(async() => {
fakeUsersService = {
findWithMail:() => Promise.resolve([]),
create:(email:string,password:string) =>
Promise.resolve({email,password} as User),
}
...
try to resolve the promise to be null not [] (empty array) or change your if(users) on your auth.service to be if(users.length > 0), why? it because empty array means to be thruthy value so when run through this process on your auth.service
if(email) {
const users = await this.userService.findWithMail(email);
// on this part below
if(users) {
throw new BadRequestException('email in use');
}
}
the 'users' executed to be truthy value so it will invoke the error. I hope my explanation will help you, thank you

MongooseError: Query was already executed:

I'm trying to update the document but the error says the query has already been executed.
MongooseError: Query was already executed: footballs.updateOne({ date: 'January 4' }, {})
app.post('/api/bookslot', async (req, res) => {
console.log(req.body);
try {
const token = req.headers['x-access-token'];
const decoded = jwt.verify(token, 'secret123');
const email = decoded.email;
const user = await UserModel.findOne({ email: email });
let sportname = req.body.selectedSport.toLowerCase();
const time = req.body.slotTime;
const seats = req.body.availableSeats - 1;
if (!sportname.endsWith('s')) {
sportname = sportname.concat('s');
}
const NewSlotModel = mongoose.model(sportname, slotSchema);
var update = {};
update[time] = seats - 1;
console.log(update);
const a = await NewSlotModel.updateOne(
{ date: req.body.slotDate },
{ $set: update },
function (err, success) {
if (err) return handleError(err);
}
);
return res.json({ status: 'ok' });
} catch (e) {
console.log(e);
res.json({ status: 'error' });
}
});
where am I going wrong?
You are using both async/await and callbacks in your code, causing mongoose to throw an error.
The actual effect of using them both is exactly the error type that you are receiving:
Query was already executed
Mongoose v6 does not allow duplicate queries.
Mongoose no longer allows executing the same query object twice. If
you do, you'll get a Query was already executed error. Executing the
same query instance twice is typically indicative of mixing callbacks
and promises, but if you need to execute the same query twice, you can
call Query#clone() to clone the query and re-execute it. See gh-7398
Duplicate Query Execution
To fix the issue, just remove the third argument from the await
NewSlotModel.updateOne
Making it:
const a = await NewSlotModel.updateOne(
{ date: req.body.slotDate },
{ $set: update }
);
Mongoose v6. Don't support callbacks any longer.. check the image.
const productCount = await Product.countDocuments((count) => count) BAD
const productCount = await Product.countDocuments(); GOOD

Response returns null data because query takes time as it is executing multiple times in node

Route.js
router.post('/social/google', auth, async (req, res, next) => {
try {
let social = await socialHandler.addSocial(req.body, req.userData.id)
let googleToken = await socialHandler.getSocial(req.userData.id)
let googleData = await socialHelper.googleToken(googleToken.google_access_token)
var nameArray = googleData.connections
let result;
let promises = [];
nameArray.map(async (element) => {
console.log('element')
console.log(element)
if (element.emailAddresses || element.phone_number) {
promises.push(await socialHandler.getUser(emailAddresses[0].value,
phoneNumbers[0].canonicalForm)); ===>> It takes long time to execute as it run multiple time so it execute after response
}
let obj = {
id: (element.names && element.names[0].metadata.source.id) ? element.names[0].metadata.source.id : '',
name: (element.names && element.names[0].displayName) ? element.names[0].displayName : '',
email: element.emailAddresses ? element.emailAddresses[0].value : '',
phone_number: element.phoneNumbers ? element.phoneNumbers[0].canonicalForm : '',
common: ''
}
promises.push(obj)
console.log(promises)
result = await Promise.all(promises);
})
req.data = promises ==> It returns null
req.status = 200
req.message = 'Google Contacts Fetched Succcessfully'
next()
}
I check socialHandler.getUser if user is saved or not. It works properly but what issue I got query can run n number of times depends on how many users are there. Query is taking time so it returns result null and after it returns query runs . Can someone help me out . I have tried async await also but await does not works

save method in mongodb/mongoose

This is my first question here. I tried to save document in my collection, but it doesn't work. Response of function is exactly like I want, but it doesn't save in my db. In another controller (createRoom) foundUser.save() it works, but in this controller it doesn't. Thanks in advance!
I am using mongodb/mongooose and express.
const removeRoom = async (req,res,next) => {
const {roomId, userData} = req.body;
const { userId, token } = userData;
let foundUser;
let updatedRooms;
let indexOfNamespaces;
try {
foundUser = await User.findById(userId)
foundUser.namespaces.forEach((ns,i1)=>{
updatedRooms = ns.rooms.filter((room,i2) => {
if(room.id === roomId){
indexOfNamespaces = i1;
}
return room.id !== roomId
})
})
foundUser.namespaces[indexOfNamespaces].rooms = updatedRooms;
console.log(foundUser);
await foundUser.save();
} catch (err) {
console.log(err);
const error = new HttpError('Sth went wrong [removeRoom]', 500);
return next(error);
}
res.status(201).json({updatedNamespaces: foundUser.namespaces});
}
Mongoose does some optimizations where it will only actually save a field if it "changes". In this case you are modifyting an array, but the array is still the "same" array as in it still === (equals) the previous array. You need to use a new array to replace namespaces.
For example:
foundUser.namespaces = [
...foundUser.namespaces.slice(0, indexOfNamespaces),
{ ...foundUser.namespaces[indexOfNamespaces], rooms: updatedRooms },
...foundUser.namespaces.slice(indexOfNamespaces + 1)
]
Now, when you save Mongoose will see a "new" array that !== (does not equal) the previous array because it is a new instance and it will save it.

What is the difference between find and findOne in this case? Why it does not work? [duplicate]

This question already has answers here:
find() and findOne() methods in MongoDB showing different results
(6 answers)
Closed 3 years ago.
I have this sort of code. When I search find()... It does not work while searching with findOne()... is working.
Thanks for your answer!
const task = await Task.find({_id, owner: req.user._id}) //This does not work
const task = await Task.findOne({_id, owner: req.user._id}) // This works
router.patch('/tasks/:id', auth, async (req, res) => {
const updates = Object.keys(req.body) // The opposite is Object.values()
const _id = req.params.id
const allowedUpdates = ['description', 'completed']
const isValidOperation = updates.every((update) => allowedUpdates.includes(update))
if (!isValidOperation) {
return res.status(400).send({error: 'Invalid update!'})
}
try {
const task = await Task.findOne({_id, owner: req.user._id})
if (!task) {
return res.status(404).send()
}
updates.forEach((update) => task[update] = req.body[update])
await task.save()
res.send(task)
} catch (e) {
res.status(500).send(e)
}
})
at first, you should use the same syntax to show an error when it coming it the no result may be because the condition isenter image description here not an achievement.

Categories

Resources