第103天 你是如何更好地处理Async/Await的异常的?
我一般直接在await后面的Promise对象上使用catch方法;不过更优雅的方式应该是对promise对象进行一层包装,通过返回值判断是否有异常,如:
// 对Promise对象进行一层包装,将异常值和成功结果一起返回
function wrapper(promise) {
  return promise
    .then(res => [null, res])
    .catch(err => [err, null])
}
function sleep(t) {
  return new Promise((resolve, reject) => {
    if (t < 1000) {
      reject('123')
    } else {
      setTimeout(() => {
        resolve()
      }, t)
    }
  })
}
async function delay() {
  let [err, res] = await wrapper(sleep(100))
  if (err) {
    console.log(`error: ${err}`)
  }
}
delay()
@xxf1996 你好,
关于你描述的错误处理方法,我并没有发现它的优雅之处。
考虑以下两份代码,我认为上面的代码更为优雅一些。
try {
    const data = await asyncFn()
    /* do sth w/ data */
} catch (err) {
    /* handle err */
}
const [data, err] = await asyncFn()
if (data) {
    /* do sth w/ data */
} else {
    /* handle err */
}
@xxf1996 你好,
关于你描述的错误处理方法,我并没有发现它的优雅之处。
考虑以下两份代码,我认为上面的代码更为优雅一些。try { const data = await asyncFn() /* do sth w/ data */ } catch (err) { /* handle err */ }const [data, err] = await asyncFn() if (data) { /* do sth w/ data */ } else { /* handle err */ }
嗯,确实没啥特别优雅的,都需要套一层;一般情况用try/catch就够用了。
@xxf1996 你好,
关于你描述的错误处理方法,我并没有发现它的优雅之处。
考虑以下两份代码,我认为上面的代码更为优雅一些。try { const data = await asyncFn() /* do sth w/ data */ } catch (err) { /* handle err */ }const [data, err] = await asyncFn() if (data) { /* do sth w/ data */ } else { /* handle err */ }
玩node时。。他那么写还是比较常见的。。
@HCLQ
玩node时。。他那么写还是比较常见的。。
你是指 callback 中使用的 error-first 的错误处理方式吗?
functionWithCallback(..., function (err, data) {
    if (err) ...
    else ...
})
这种的确是常见,但是返回 Promise<[Error?, any?]> 的 async function 我真的没见过。或许可以给些例子?
@t532 包装写法可以省去你每次都需要再具体位置使用try ... catch或者.catch
await加try...catchasync function f() {
  try {
    let response = await fetch('http://no-such-url');
  } catch(err) {
    alert(err); 
  }
}
f();
async函数必然返回一个Promose,所以可以在执行async函数后加入.catchasync function f() {
  let response = await fetch('http://no-such-url');
}
f().catch(alert); 
unhandledrejection进行捕获window.addEventListener('unhandledrejection', function(event) {
  // the event object has two special properties:
  alert(event.promise); // [object Promise] - the promise that generated the error
  alert(event.reason); // Error: Whoops! - the unhandled error object
});
参考:
Async/await
关于两种写法优劣的,可以看看一下这篇外网文章How to write async await without try-catch blocks in Javascript。文章作者可能也是这种写法的创始者,他从go-lang编程中获得灵感,使用ts造了相应的轮子:await-to-js。
@EmiyaYang
关于两种写法优劣的,可以看看一下这篇外网文章How to write async await without try-catch blocks in Javascript。文章作者可能也是这种写法的创始者,他从go-lang编程中获得灵感,使用ts造了相应的轮子:await-to-js。
Golang 的错误处理方式好处是可以不用被强迫在错误发生时即进行处理,然而却无法在同一个地方 handle 多个错误(除了 ErrorGroup 这种丑陋的方式)。所以两种方式都各有优劣,还是看需求用吧。这篇博客的作者也写了:
This post is just a different way of looking on async/await error handling. It should not be used as a goto for every async/await function you write and in a lot cases having a single catch at the top will do just fine. Sometimes we don't want to expose the error object of the implementation of the model and want instead to provide a custom error object masking the underlying mongoose error implementation.
try-catch
我使用装饰器来处理, 避免重复写trycatch
主要针对vue这样的框架,通过class使用的
使用方法
const CatchError = (errFunc?: ((ctx: any, err: Error) => void)): MethodDecorator => {
  return function (target, propertyKey, descriptor: PropertyDescriptor) {
    let method = descriptor.value
    descriptor.value = async function (...args) {
      try {
        await method.apply(this, args)
      } catch (error) {
        if (errFunc) {
          errFunc(this, error)
        } else {
          console.log(`[Error] 统一处理${String(propertyKey)}`, error)
        }
      }
    }
  }
}
const wrapper = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() < 0.2) {
        resolve({
          name: 'world'
        })
      } else {
        reject(new Error('is a err'))
      }
    }, 1000)
  })
}
class Test {
  name: string = 'hello'
  // 自己处理错误
  @CatchError((ctx, err) => {
    console.log('自己处理错误:', err,'\nthis指向:', ctx)
  })
  async runSelf () {
    const data = await wrapper()
    console.log('收到数据:', data)
  }
  // 统一处理错误
  @CatchError()
  async runCommon () {
    const data = await wrapper()
    console.log('收到数据:', data)
  }
}
const t = new Test()
t.runSelf()
t.runCommon()
@xxf1996 写的挺好的,每次写代码的时候总感觉大括号的嵌套层数和代码的不稳定性成正比。。
Most helpful comment
我一般直接在
await后面的Promise对象上使用catch方法;不过更优雅的方式应该是对promise对象进行一层包装,通过返回值判断是否有异常,如:参考文档:async/await 优雅的错误处理方法 - 掘金