await的错误处理问题,一个issue引发的ts社区的讨论

前提

大家都知道try-catch对异步流程中的错误有时候是捕捉不到的,eg:

function func1() {
  try {
    func2();
  } catch (error) {
    console.log('error');
  }
}

function func2() {
  setTimeout(() => {
    throw new Error('error')
  }, 1000)
}

func1();

执行这些代码,你会发现过了一秒后程序直接报错,console.log('error')并没有执行,也就是func1并没有捕捉到func2的异常。这就是异步的问题所在。

我们一般的做法就是直接加上async/await:

async function func1() {
  try {
    await func2();
  } catch (error) {
    console.log('error');
  }
}

function func2() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject()
    }, 1000)
  })
}

func1();

func2被Promise封装,reject后会被func1的try-catch捕获到,像现在的axios很多库都已经封装好Promise方便直接await。

问题

这里我们要讨论的就是对await到底用try-catch,if(err)的方式。

if(err)的方式在node中的很多异步回调函数中很多场景都用到,err永远是node结果回调函数的第一个参数,但此情景又与node中的不一样。对await的错误处理要想获取到err,需要额外增加一个函数,用try-catch捕获到的err return出去。
eg:
每次使用都要引用errCatch()。

async function errCatch(asyncFunc) {
    try {
        let res = await asyncFunc();
        retrun [null, res]
    } catch (error) {
        return [error,null];
    }
}

这样func1就要写成:

async function func1() {
  let [err, res] = errCatch(asyncFunc);
  if (err) {
    //.....
  }
}

那么这种写法比直接try-catch有什么好处,第一点就是更加语义化了,这个问题在deno社区就这个问题的ts写法引起过讨论,可以看看这个issue:

https://github.com/denoland/deno_std/issues/525

在这里插入图片描述
在这里插入图片描述
第一个意思是 Golang 只需要处理 return 出来的错误,很少有运行时错误,即便有,也是可以在 defer 统一处理。 第二个意思是,像一个读流 copy 到另一个写流。假如因为某个异常中断的话,我们需要知道是读取到了第 n 的细节中断的,然后错误是什么。这样的场景就适合这样使用。(具体可以再copy中实现)
(但我觉得这个其实是特殊场景,用try-catch也能控制)
在这里插入图片描述
其实一个老哥也指出传统的promise做法,因为await的错误不捕获会导致整个async函数的中断,用catch也能避免中断并捕捉到错误。

开issue的作者是说使用这种style的原因是这样的:
在这里插入图片描述
最后维护者是这么说的:
在这里插入图片描述
大概意思就是说
“我们可以在utils/async.ts中实现这一点,供我们自己使用,并作为用户的一个选项提供。但正如我所说,在更多的情况下,Try-Catch是进入JS的正确方法,或者提供像上面提到的@J-F1这样的catch处理程序。
(作为一个夸张的例子,C中的全局errno在某些情况下可能更干净,但是我们在JS中不做这样的构造,因为它违背了预期的语言用法。
如果您认为这些助手非常有用,那么如果您能够贡献并提交一个pr来将它们添加到utils/async.ts中,这将是非常好的,并且有可能在将来说服更多的JS开发人员尝试这样的处理方式,这样有一天它就足够惯用了。
(我目前受到一些法律纠纷的约束,因此不幸的是,我自己做不到。)”

其实上面issue作者举的那个例子就是说在官方库中有些错误不用被处理,只是做检测的情况下,不用写try-catch,catch的e就更不用写上了,用了helper的style后,可以直接这样写:

const statInfo = await maybe(stat(fn)); // yields FileInfo | null 
const mode = statInfo ? statInfo.mode : null;

看上去更加舒服???

知乎上关于node.js 应该 return new Error() 还是 throw new Error()的讨论

后面突然想到了之前看到的一个话题的讨论,其实和这个很像,某种程度甚至是一样的了:
node.js 应该 return new Error() 还是 throw new Error()?

参考:
https://www.zhihu.com/question/323618147
https://juejin.im/post/5d25b39bf265da1bb67a4176
https://github.com/denoland/deno_std/issues/525
http://es6.ruanyifeng.com/#docs/async

原文地址:https://www.cnblogs.com/zhangmingzhao/p/11182057.html