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
.
ES6 Promise's catch handler and work harmoniously with "await/async", providing a proper solution and cleaner code:
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. In this case, it is better to throw because (1) this operation (creating a user) is not expected to failed, and (2) you not able to continue anyway -- the operation has errored out:
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.
var
, knowing the variable would be hoisted. Is that "wrong"? – Macrophagethis.User.create()
then you wouldn't put anything else inside the try/catch. But, it's also a perfectly reasonable design to put a whole bunch of logic inside a try block. It all depends upon how/where you want to handle an error and how you want to design your exception handling code and what makes sense for a given operation. There is no generic best practice. The ONE generic best practice is to make sure you catch and handle all errors in some appropriate way. – Avitzurasync/await
is part of ES2017 (this year's release), not ES6 (which was released two years ago). – Unequaledtry
block, and that codeError
s, (TypeError
,ReferenceError
, etc), that will becatch
ed, which could produce unexpected behavior if you're expecting to onlycatch
promise rejections. – Evzone