让Async Await的错误捕捉处理更优雅!

ES7的async/await应该在大量场景下都普及了,但错误处理往往会让业务逻辑略微混乱不那么优雅。

不得不说async/await是个大杀机,原来用JS编写大量的异步函数很容易陷入毁掉地狱callback hell,现在使用基于Promise的async/await让异步方法同步化,使得业务逻辑更清晰,我在大约去年初开始在项目中大量使用和重构。

但是与async/await配合使用的错误处理try/catch️又会一定程度上影响到代码的编写体验,try/catch 存在的一个问题是,捕获到的错误只在 catch 后面的块内可见,而成功返回的结果又只能在try里可见。例如:

try{
    let result = await promise
}catch(e){
  
}
//这里无论是result还是e都是undefined

所以我们不得不去这么编写:

try{
    let result = await promise
    domething(result)
}catch(e){
    domething(e)
}

简单的逻辑还好,但是当try的代码块里存在很多的业务逻辑时,catch的捕捉错误就会不那么直观了。

前不久看了一些开发者的议论,我借鉴了一下同行的方案并小作改进,目前使用的方案是下面这种:

先写一个通用处理promise函数错误的函数promiseHandler:

const promiseHandler = async (promise)=>{
    try{
        let result = await promise
        return [null,result]
    }catch(err){
        return [err,null]
    }
}

这个函数将promise方法作为参数执行,在函数内部try/catch,返回一个数组。

当没有错误时,返回的数组第一个项是null,第二个是成功执行异步函数返回的值。

而一旦捕捉到错误时,返回的数组第一个项则是错误,第二个就是null。

这样在实际编写业务逻辑时,配合上ES6的解构赋值,很轻易的写出容易理解的逻辑。

const promiseHandler = async (promise)=>{
    try{
        let result = await promise
        return [null,result]
    }catch(err){
        return [err,null]
    }
}

const main = async ()=>{
    let [err,result] = promiseHandler(promise)
    if(err) return handlerErr(err)
    handlerResult(result)
}

main()

改进后的逻辑逐行阅读,维护起来也会效率更高。不过仍然会存在一点问题是,每次promiseHandler的参数是一整个promise对象,这稍微有点不符合函数式编程的风格,改进promiseHandler如下:

const promiseHandler = promise => {
  return async (...args) => {
    {
      try {
        let result = await promise.apply(this, args)
        return [null, result]
      } catch (err) {
        return [err, null]
      }
    }
  }
}

const main = async ()=>{
    let [err,result] = promiseHandler(promise)(args)
    if(err) return handlerErr(err)
    handlerResult(result)
}

main()

至此,清晰的处理异步逻辑的通用模块编写完毕。不一定是最好的方案,如果有其他方案欢迎和我交流。