huruji / blog

红日初升,其道大光:sun_with_face::house_with_garden:请star或watch,不要fork

Home Page:https://juejin.im/user/5894886f2f301e00693a3e49/posts

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

async await 的错误处理

huruji opened this issue · comments


async await 从语法层面给人一种非常直观的方式,可以让我们避免 callback hell 与 Promise hell 。

async function getUserInfo() {
	const id = await request.getCurrentId()
	const info = await request.getUserInfo(id)

 	return info
}

但是每一步 await 的都可能出错,为了捕获这些错误,我们使用 try...catch...

async function getUserInfo (cb) {
  try {
     const id = await request.getCurrentId()
  } catch(e) {
     return cb('an error in getCurrentId')
  }
  
  try {
    const info = await request.getUserInfo(id)
  } catch(e) {
    return cb('an error in getUserInfo')
  }

  return cb(null, info)
}

这样写一眼看上去代码异常丑陋而且不直观,国外大神 Dima 在他的文章 how-to-write-async-await-without-try-catch-blocks-in-javascript 中提到了一种解决方案,因为 await 实际上等待的是一个 Promise,因此可以使用一个函数包装一个来符合 error first 的原则,从而避免 try...catch...

function to (promise) {
  return promise.then(data => {
      return [null, data]
  }).catch(err => [err])

}

通过这个函数包装一下上面的例子

async function getUserInfo () {
  let err, id, info; 
  [err, id] = await to(request.getCurrentId())
  if(err) return console.error(err)
  
  [err, info] = await to(request.getUserInfo(id))
  if(err) return console.error(err)

  return info
}

基于这种思路,可以想到直接在每一步 await 的时候都单独 catch, 最后在最外层捕获 error

async function getUserInfo() {
  try {
    const id = await request.getCurrentId().catch(err => Promise.reject('an error in getCurrentId'))
    const info = await request.getUserInfo(id).catch(err => Promise.reject('an error in getUserInfo'))
  } catch(err) {
    errorHandle(err)
  }
}

在实际编码中,我们当然想要一个公共的 error 处理函数,不过如果你的业务太复杂了,偶尔中途需要有额外的处理逻辑也没关系,别忘了 Promise.reject() 啥都可以作为参数:

async function getUserInfo() {
  try {
    const id = await request.getCurrentId().catch(err => Promise.reject('an error in getCurrentId'))
    const info = await request.getUserInfo(id).catch(err => Promise.reject(() => {
    doSomething()
    anotherErrorHandler()
})
  } catch(err) {
    if (typeof err === 'function') err()
    else errorHandle(err)
  }
}

Dima 的处理方式已经很不错了,市面上有非常多的基于这种**的库,可以在 npm 上 搜索,如果简单拓展下自定义 error 的信息(如code,msg),是否采用 errorFirst 的惯例,如下:

function to (promise, errorProps = {}, errorFirst = true) {
	return promise.then((data) => errorFirst ? [null, data] : [data, null])
			  .catch(err => {
				  if(errorProps) Object.assign(err, errorProps)
				  errorFirst ? [err, undefined] : [undefined, err]
			  })
  }

大概关于 async await 的错误处理就总结如上了,以后遇到更好地处理方式再说。