Using mongoose promises with async/await - javascript

I'm trying to get the hang of using Mongoose promises with the async/await functionality of Node.js. When my function printEmployees is called I want to save the list of employees which are queried by the orderEmployees function. While, the console.log statement inside orderEmployees returns the expected query, the console.log inside of printEmployees returns undefined, suggesting that I'm not returning the promise correctly.
I'm new to promises so entirely possible that I'm not correctly understanding the paradigm... any help is much appreciated.
printEmployees: async(company) => {
var employees = await self.orderEmployees(company);
// SECOND CONSOLE.LOG
console.log(employees);
},
orderEmployees: (companyID) => {
User.find({company:companyID})
.exec()
.then((employees) => {
// FIRST CONSOLE.LOG
console.log(employees);
return employees;
})
.catch((err) => {
return 'error occured';
});
},

In order to make orderEmployees behave like async functions, you have to return the resulting promise. There are two rules to follow when using promises without async/await keywords:
A function is asynchronous if it returns a Promise
If you have a promise (for example returned by an async function) you must either call .then on it or return it.
When you are using async/await then you must await on promises you obtain.
This said you will notice that you do not return the promise generated inside orderEmployees. Easy to fix, but its also easy to rewrite that function to async too.
orderEmployees: (companyID) => {
return User.find({company:companyID}) // Notice the return here
.exec()
.then((employees) => {
// FIRST CONSOLE.LOG
console.log(employees);
return employees;
})
.catch((err) => {
return 'error occured';
});
},
or
orderEmployees: async(companyID) => {
try {
const employees = await User.find({company:companyID}).exec();
console.log(employees);
return employees;
} catch (err) {
return 'error occured';
}
},
PS: the error handling is somewhat flawed here. We usually do not handle errors by returning an error string from a function. It is better to have the error propagate in this case, and handle it from some top-level, UI code.

You need to return your Promise.
Currently, you are awaiting on a function that returns undefined.
await only actually "waits" for the value if it's used with a Promise.
Always keep in mind that you can only await Promises or async functions, which implicitly return a Promise1.
orderEmployees: (companyID) => {
return User.find({ company:companyID }).exec()
}
Also really important, you should throw instead of return in your .catch handler. Returning from within a .catch handler will cause the promise chain to trigger it's .then instead of it's .catch thus breaking the error handling chain.
Better yet, don't include .catch at all and let the the actual error bubble up the promise chain, instead of overriding it with your own non-descriptive 'error occured' message.
Error conditions should throw the error, not return it.
1 You can also await non-Promises, but only for values that are evaluated synchronously.

You are not returning a Promise from orderEmployees.
printEmployees: async(company) => {
var employees = await self.orderEmployees(company);
// SECOND CONSOLE.LOG
console.log(employees);
},
orderEmployees: (companyID) => {
return User.find({company:companyID})
.exec()
.then((employees) => {
// FIRST CONSOLE.LOG
console.log(employees);
return employees;
})
.catch((err) => {
return 'error occured';
});
},

You need to return a Promise from orderEmployees
orderEmployees: companyId => User.find({ companyId }).exec()
If you want to do some error handling or pre-processing before you return then you can keep your code as is but just remember to return the result (promises are chainable).

if you're going to use async/await then it works like this.
await in front of the function that returns a promise.
async in front of the wrapping function.
wrap the function body inside try/catch block.
Please have a look on this function, it is a middleware
before i execute a specific route in express.
const validateUserInDB = async (req, res, next) => {
try {
const user = await UserModel.findById(req.user._id);
if (!user) return res.status(401).json({ message: "Unauthorized." });
req.user = user;
return next();
} catch (error) {
return res.status(500).json({ message: "Internal server error." })
}
}
The code after await is waiting the promise to be resolved.
Catch block catches any error happened inside the try block even if the error that is triggered by catch method comes from awaiting promise.

Related

Firebase admin sdk calling the wrong catch

I am trying to add a new user with firebase-admin and then to save a new document in a custom collection.
Sample code following:
admin.auth().createUser(user)
.then((record) => {
user.uid = record.uid;
userCollection.doc(record.uid).set({...user})
.then(writeResult => {
resolve();
})
.catch(reason => {
reject(reason)
});
})
.catch((err) => {
reject(err);
});
The problem is, if the userCollection.doc(record.uid).set({...user}) fails, I expect the nested catch (with reason as param) to be called. Instead, always the outer one is called (with err as param).
Is there something wrong with the SDK or am I doing something wrong?
Thank you
This is because you don't return the promise returned by userCollection.doc(record.uid).set() and therefore you don't return the promises returned by the subsequent then() and catch() methods. In other words you don't return the promises chain.
But, actually, you should chain your Promises as follows and avoid a then()/catch() pyramid.
admin
.auth().createUser(user)
.then((record) => {
user.uid = record.uid;
return userCollection
.doc(record.uid)
.set({ ...user })
})
.catch((err) => {
// Here you catch the potential errors of
// the createUser() AND set() methods
console.log(JSON.stringify(err));
});
More details here, here and here.

How exactly works this async\await code in my Angular application? Why the colled method doesn't explicitly return a Promise?

I am not so into RxJS and I am finding some problems to understand this piece of code retrieved into an Angular project on which I am working.
First of all into a component TypeScript code I have this method:
async post(): Promise<void> {
this.submitted.next(true);
try {
await this.setAddress();
this.activeModal.close();
} catch (e) {
console.error('Storage upload error', e);
this.submitted.next(false);
}
}
As you can see this method have async prefix because into the try block it contains these 2 rows:
await this.setAddress();
this.activeModal.close();
from what I have understand (please correct me if I am doing wrong assertion) basically the await in front of this.setAddress() it means: await that this method call end, when it is completed executed the following operation (that in this case close a modal window).
From what I have understand it replave the then() method handling a Promise resolution. Is it correct or not?
So my doubt is: have my setAddress() method return a Promise? In my specific case setAddress() method is used to call a service method saving some data on the database and have this code:
async setAddress(): Promise<void> {
try {
const c: Address = {
companyName:this.addressFormGroup.get('companyName').value,
street: this.addressFormGroup.get('street').value,
city: this.addressFormGroup.get('city').value,
zipCode: this.addressFormGroup.get('zipCode').value,
notes: this.addressFormGroup.get('notes').value,
};
//save/update record
await this.userService.setUserAdresss(this.currentUserUID,this.addressType,c);
this.success = true;
if (!this.isEditMode) {
this.addressFormGroup.reset();
}
} catch (e) {
console.error(e);
} finally {
this.submitted.next(false);
}
}
And here I have a lot of doubts on how it works...ok the method signature:
async setAddress(): Promise<void> {
seems to return a Promise (why ? what it means?). But where is it effectivelly returning a Promise? In the code of this method I can't find that it is returning a Promise nowhere. It seems to me that it is returning nothing because it is not containing the return statement !!!
My only interpretation is the following one (but it is my idea and probably totally wrong): also if it is not explicitly returning nothing it have a Promise as method returned type. So it means that at the end of the method execution TypeScript automatically return an "empty" Promise that simply means: "method execution completed".
I am absolutly not sure, this is the only explaination that I can give to this code.
How it wxactly works?
Your assumptions are correct.
A function declared with the async keyword will return a Promise that is completed when the function has finished its execution.
The await keyword is equivalent to using the then method with the remaining lines of codes as the callback function.
Using try/catch/finally arround an await is equivalent to using the catch/finally method on the promise.
This is your code written with promises instead of async/await :
post(): Promise<void> {
this.submitted.next(true);
return this.setAddress()
.then(() => this.activeModal.close())
.catch((e) => {
console.error('Storage upload error', e);
this.submitted.next(false);
});
}
setAddress(): Promise<void> {
const c: Address = {
companyName:this.addressFormGroup.get('companyName').value,
street: this.addressFormGroup.get('street').value,
city: this.addressFormGroup.get('city').value,
zipCode: this.addressFormGroup.get('zipCode').value,
notes: this.addressFormGroup.get('notes').value,
};
//save/update record
return this.userService.setUserAdresss(this.currentUserUID,this.addressType,c)
.then(() => {
this.success = true;
if (!this.isEditMode) {
this.addressFormGroup.reset();
}
})
.catch((e) => console.error(e))
.finally(() => this.submitted.next(false));;
}

Improved way to deal with callbacks inside promises

I have the following code that uses callbacks inside promises:
const clue = 'someValue';
const myFunction = (someParam, callback) => {
someAsyncOperation(someParam) // this function returns an array
.then((array) => {
if (array.includes(clue)){
callback(null, array); // Callback with 'Success'
}
else{
callback(`The array does not includes: ${clue}`); // Callback with Error
}
})
.catch((err) => {
// handle error
callback(`Some error inside the promise chain: ${err}`) // Callback with Error
})
}
and call it like this:
myFunction (someParam, (error, response) => {
if(error) {
console.log(error);
}
else {
// do something with the 'response'
}
})
Reading some documentation, I found that there is some improved way to do this:
const myFunction = (someParam, callback) => {
someAsyncOperation(someParam) // this function returns an array
.then((array) => {
if (array.includes(clue)){
callback(array);
}
else{
callback(`The array does not includes: ${clue}`);
}
}, (e) => {
callback(`Some error happened inside the promise chain: ${e}`);
})
.catch((err) => {
// handle error
callback(`Some error happened with callbacks: ${err}`)
})
}
My question:
In the sense of performance or best practices, it's okay to call the 'callback' function inside the promise as the two ways show or I'm doing something wrong, I mean some promise anti-pattern way ?
This seems really backwards and takes away from the benefits of promises managing errors and passing them down the chain
Return the asynchronous promise from the function and don't interrupt it with callbacks. Then add a catch at the end of the chain
const myFunction = (someParam) => {
// return the promise
return someAsyncOperation(someParam) // this function returns an array
.then((array) => {
return array.includes(clue) ? array : [];
});
}
myFunction(someParam).then(res=>{
if(res.length){
// do something with array
}else{
// no results
}
}).catch(err=>console.log('Something went wrong in chain above this'))
Do not use callbacks from inside of promises, that is an anti-pattern. Once you already have promises, just use them. Don't "unpromisify" to turn them into callbacks - that's moving backwards in code structure. Instead, just return the promise and you can then use .then() handlers to set what you want the resolved value to be or throw an error to set what you want the rejected reason to be:
const clue = 'someValue';
const myFunction = (someParam) => {
return someAsyncOperation(someParam).then(array => {
if (!array.includes(clue)){
// reject promise
throw new Error(`The array does not include: ${clue}`);
}
return array;
});
}
Then, the caller would just do this:
myFunction(someData).then(array => {
// success
console.log(array);
}).catch(err => {
// handle error here which could be either your custom error
// or an error from someAsyncOperation()
console.log(err);
});
This gives you the advantage that the caller can use all the power of promises to synchronize this async operation with any others, to easily propagate errors all to one error handler, to use await with it, etc...

How do I handle an error and then immediately break out of a promise chain?

So I have an Express app that uses middleware to parse JSON POST requests and then populate a req.body object. Then I have a promise chain that validates the data against a schema using Joi, and then stores it in a database.
What I would like to do is check if an error was thrown after one of these processes, handle it appropriately by sending a status code, then COMPLETELY ABORT the promise chain. I feel like there should be some EXTREMELY CLEAN AND SIMPLE way to do this, (perhaps some sort of break statement?) but I can't find it anywhere. Here is my code. I left comments showing where I hope to abort the promise chain.
const joi = require("joi");
const createUserSchema = joi.object().keys({
username: joi.string().alphanum().min(4).max(30).required(),
password: joi.string().alphanum().min(2).max(30).required(),
});
//Here begins my promise chain
app.post("/createUser", (req, res) => {
//validate javascript object against the createUserSchema before storing in database
createUserSchema.validate(req.body)
.catch(validationError => {
res.sendStatus(400);
//CLEANLY ABORT the promise chain here
})
.then(validatedUser => {
//accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
})
.catch(error => {
res.sendStatus(500);
//CLEANLY ABORT the promise chain here
})
//Only now, if both promises are resolved do I send status 200
.then(() => {
res.sendStatus(200);
}
)
});
You can't abort a promise chain in the middle. It's going to either call a .then() or a .catch() later in the chain (assuming there are both and assuming your promises resolve or reject).
Usually, the way you handle this is you put one .catch() at the end of the chain and it examines the type of error and takes appropriate action. You don't handle the error earlier in the chain. You let the last .catch() handle things.
Here's what I would suggest:
// helper function
function err(status, msg) {
let obj = new Error(msg);
obj.status = status;
return obj;
}
//Here begins my promise chain
app.post("/createUser", (req, res) => {
//validate javascript object against the createUserSchema before storing in database
createUserSchema.validate(req.body).catch(validationError => {
throw err("validateError", 400)
}).then(validatedUser => {
//accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
}).catch(err => {
throw err("createUserError", 500);
});
}).then(() => {
// success
res.sendStatus(200);
}).catch(error => {
console.log(error);
if (error && error.status) {
res.sendStatus(error.status);
} else {
// no specific error status specified
res.sendStatus(500);
}
});
});
This has several advantages:
Any error propagates to the last .catch() at the end of the chain where it is logged and an appropriate status is sent in just one place in the code.
Success is handled in just one place where that status is sent.
This is infinitely extensible to more links in the chain. If you have more operations that can have errors, they can "abort" the rest of the chain (except the last .catch() by just rejecting with an appropriate error object).
This is somewhat analogous to the design practice of not having lots of return value statements all over your function, but rather accumulating the result and then returning it at the end which some people consider a good practice for a complicated function.
When debugging you can set breakpoints in one .then() and one .catch() to see the final resolution of the promise chain since the whole chain goes through either the last .then() or the last .catch().
.catch returns a resolved Promise by default. You want a rejected Promsise. So, you should return a rejected promise from inside the .catch, so that future .thens won't execute:
.catch(validationError => {
res.sendStatus(400);
return Promise.reject();
})
But note that this will result in a console warning:
Uncaught (in promise) ...
So it would be nice to add another .catch to the end, to suppress the error (as well as catch any other errors that come along):
const resolveAfterMs = ms => new Promise(res => setTimeout(() => {
console.log('resolving');
res();
}), ms);
console.log('start');
resolveAfterMs(500)
.then(() => {
console.log('throwing');
throw new Error();
})
.catch(() => {
console.log('handling error');
return Promise.reject();
})
.then(() => {
console.log('This .then should never execute');
})
.catch(() => void 0);
If you want to avoid all future .thens and future .catches, I suppose you could return a Promise that never resolves, though that doesn't really sound like a sign of a well-designed codebase:
const resolveAfterMs = ms => new Promise(res => setTimeout(() => {
console.log('resolving');
res();
}), ms);
console.log('start');
resolveAfterMs(500)
.then(() => {
console.log('throwing');
throw new Error();
})
.catch(() => {
console.log('handling error');
return new Promise(() => void 0);
})
.then(() => {
console.log('This .then should never execute');
})
.catch(() => {
console.log('final catch');
});
A cleaner solution for what you are trying to accomplish might be to use express-validation, which is a simple wrapper around joi that provides you with express middleware for validation of the body, params, query, headers and cookies of an express request based on your Joi schema.
That way, you could simply handle any Joi validation errors thrown by the middleware within your "generic" express error handler, with something like:
const ev = require('express-validation');
app.use(function (err, req, res, next) {
// specific for validation errors
if (err instanceof ev.ValidationError)
return res.status(err.status).json(err);
...
...
...
}
If you don't want to use the express-validation package, you could write your own simple middleware that does more or less the same thing, as described here (see example here).
One strategy is to separate your error handling in subpromises which have their individual error handling. If you throw an error from them, you'll bypass the main promise chain.
Something like:
return Promise.resolve().then(() => {
return createUserSchema.validate(req.body)
.catch(validationError => {
res.sendStatus(400);
throw 'abort';
});
}).then(validatedUser => {
// if an error was thrown before, this code won't be executed
// accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
}).catch(error => {
// if an error was previously thrown from `createUserSchema.validate`
// this code won't execute
res.sendStatus(500);
throw 'abort';
});
}).then(() => {
// can put in even more code here
}).then(() => {
// it was not aborted
res.sendStatus(200);
}).catch(() => {
// it was aborted
});
You can skip the Promise.resolve().then() wrapping, but it's included for illustrative purposes of the general pattern of subdividing each task and its error handling.

How to properly handle error in MongoDB bulkwrite? [duplicate]

I like the flatness of the new Async/Await feature available in Typescript, etc. However, I'm not sure I like the fact that I have to declare the variable I'm awaiting on the outside of a try...catch block in order to use it later. Like so:
let createdUser
try {
createdUser = await this.User.create(userInfo)
} catch (error) {
console.error(error)
}
console.log(createdUser)
// business
// logic
// goes
// here
Please correct me if I'm wrong, but it seems to be best practice not to place multiple lines of business logic in the try body, so I'm left only with the alternative of declaring createdUser outside the block, assigning it in the block, and then using it after.
What is best practice in this instance?
It seems to be best practice not to place multiple lines of business logic in the try body
Actually I'd say it is. You usually want to catch all exceptions from working with the value:
try {
const createdUser = await this.User.create(userInfo);
console.log(createdUser)
// business logic goes here
} catch (error) {
console.error(error) // from creation or business logic
}
If you want to catch and handle errors only from the promise, you have three choices:
Declare the variable outside, and branch depending on whether there was an exception or not. That can take various forms, like
assign a default value to the variable in the catch block
return early or re-throw an exception from the catch block
set a flag whether the catch block caught an exception, and test for it in an if condition
test for the value of the variable to have been assigned
let createdUser; // or use `var` inside the block
try {
createdUser = await this.User.create(userInfo);
} catch (error) {
console.error(error) // from creation
}
if (createdUser) { // user was successfully created
console.log(createdUser)
// business logic goes here
}
Test the caught exception for its type, and handle or rethrow it based on that.
try {
const createdUser = await this.User.create(userInfo);
// user was successfully created
console.log(createdUser)
// business logic goes here
} catch (error) {
if (error instanceof CreationError) {
console.error(error) // from creation
} else {
throw error;
}
}
Unfortunately, standard JavaScript (still) doesn't have syntax support for conditional exceptions.
If your method doesn't return promises that are rejected with specific enough errors, you can do that yourself by re-throwing something more appropriate in a .catch() handler:
try {
const createdUser = await this.User.create(userInfo).catch(err => {
throw new CreationError(err.message, {code: "USER_CREATE"});
});
…
} …
See also Handling multiple catches in promise chain for the pre-async/await version of this.
Use then with two callbacks instead of try/catch. This really is the least ugly way and my personal recommendation also for its simplicity and correctness, not relying on tagged errors or looks of the result value to distinguish between fulfillment and rejection of the promise:
await this.User.create(userInfo).then(createdUser => {
// user was successfully created
console.log(createdUser)
// business logic goes here
}, error => {
console.error(error) // from creation
});
Of course it comes with the drawback of introducing callback functions, meaning you cannot as easily break/continue loops or do early returns from the outer function.
Another simpler approach is to append .catch to the promise function. ex:
const createdUser = await this.User.create(userInfo).catch( error => {
// handle error
})
Cleaner code
using async/await with Promise catch handler.
From what I see, this has been a long-standing problem that has bugged (both meanings) many programmers and their code. The Promise .catch is really no different from try/catch.
Working harmoniously with await/async, ES6 Promise's catch handler provides a proper solution and make code cleaner:
const createUser = await this.User
.create(userInfo)
.catch(error => console.error(error))
console.log(createdUser)
// business
// logic
// goes
// here
Note that while this answers the question, it gobbles up the error. The intention must be for the execution to continue and not throw. In this case, it's usually always better to be explicit and return false from catch and check for user:
.catch(error => {
console.error(error);
return false
})
if (!createdUser) // stop operation
In this case, it is better to throw because (1) this operation (creating a user) is not expected to failed, and (2) you are likely not able to continue:
const createUser = await this.User
.create(userInfo)
.catch(error => {
// do what you need with the error
console.error(error)
// maybe send to Datadog or Sentry
// don't gobble up the error
throw error
})
console.log(createdUser)
// business
// logic
// goes
// here
Learning catch doesn't seem like worth it?
The cleanliness benefits may not be apparent above, but it adds up in real-world complex async operations.
As an illustration, besides creating user (this.User.create), we can push notification (this.pushNotification) and send email (this.sendEmail).
this.User.create
this.User.create = async(userInfo) => {
// collect some fb data and do some background check in parallel
const facebookDetails = await retrieveFacebookAsync(userInfo.email)
.catch(error => {
// we can do some special error handling
// and throw back the error
})
const backgroundCheck = await backgroundCheckAsync(userInfo.passportID)
if (backgroundCheck.pass !== true) throw Error('Background check failed')
// now we can insert everything
const createdUser = await Database.insert({ ...userInfo, ...facebookDetails })
return createdUser
}
this.pushNotifcation and this.sendEmail
this.pushNotification = async(userInfo) => {
const pushed = await PushNotificationProvider.send(userInfo)
return pushed
})
this.sendEmail = async(userInfo) => {
const sent = await mail({ to: userInfo.email, message: 'Welcome' })
return sent
})
Compose the operations:
const createdUser = await this.User
.create(userInfo)
.catch(error => {
// handle error
})
// business logic here
return await Promise.all([
this.pushNotification(userInfo),
this.sendEmail(userInfo)
]).catch(error => {
// handle errors caused
// by pushNotification or sendEmail
})
No try/catch. And it's clear what errors you are handling.
I usually use the Promise's catch() function to return an object with an error property on failure.
For example, in your case i'd do:
const createdUser = await this.User.create(userInfo)
.catch(error => { error }); // <--- the added catch
if (Object(createdUser).error) {
console.error(error)
}
If you don't like to keep adding the catch() calls, you can add a helper function to the Function's prototype:
Function.prototype.withCatcher = function withCatcher() {
const result = this.apply(this, arguments);
if (!Object(result).catch) {
throw `${this.name}() must return a Promise when using withCatcher()`;
}
return result.catch(error => ({ error }));
};
And now you'll be able to do:
const createdUser = await this.User.create.withCatcher(userInfo);
if (Object(createdUser).error) {
console.error(createdUser.error);
}
EDIT 03/2020
You can also add a default "catch to an error object" function to the Promise object like so:
Promise.prototype.catchToObj = function catchToObj() {
return this.catch(error => ({ error }));
};
And then use it as follows:
const createdUser = await this.User.create(userInfo).catchToObj();
if (createdUser && createdUser.error) {
console.error(createdUser.error);
}
#Bergi Answer is good, but I think it's not the best way because you have to go back to the old then() method, so i think a better way is to catch the error in the async function
async function someAsyncFunction(){
const createdUser = await this.User.create(userInfo);
console.log(createdUser)
}
someAsyncFunction().catch(console.log);
But what if we have many await in the same function and need to catch every error?
You may declare the to() function
function to(promise) {
return promise.then(data => {
return [null, data];
})
.catch(err => [err]);
}
And then
async function someAsyncFunction(){
let err, createdUser, anotherUser;
[err, createdUser] = await to(this.User.create(userInfo));
if (err) console.log(`Error is ${err}`);
else console.log(`createdUser is ${createdUser}`);
[err, anotherUser] = await to(this.User.create(anotherUserInfo));
if (err) console.log(`Error is ${err}`);
else console.log(`anotherUser is ${anotherUser}`);
}
someAsyncFunction();
When reading this its: "Wait to this.User.create".
Finally you can create the module "to.js" or simply use the await-to-js module.
You can get more information about to function in this post
await this.User.create(userInfo).then(async data => await this.emailService.sendEmail(data.email), async error => await this.sentryService.sendReport(error))

Categories