(掘金)JS错误捕获的原理部分,知道了如何使用

(掘金)JS错误捕获的原理部分,知道了如何使用

在上一篇文章中,我们详细回顾了JS错误捕获的原理,并了解了如何使用try...catch...和Promise.catch,所以现在让我们看一个稍微复杂的场景! 虽然是一个复杂的场景cocos-js 异步 个别加载失败,但其实很常见,就是一个函数中有多个后续的异步操作,而这些异步操作需要进行错误捕获。 代码说明如下:

async function stepOne() {
 // 异步操作一
}
async function stepTwo() {
 // 异步操作二
}
async function stepThree() {
 // 异步操作三
}
// 使用 try...catch...对错误统一捕获
async function asyncRun() {
 try{
  const res1 = await stepOne()
  const res2 = await stepTwo()
  const res3 = await stepThree()
 }catch(err) {
  console.log(err)
 }
}
// 使用 Promise.catch 对错误进行捕获
function runPromises() {
 stepOne()
 .then(res => {
  stepTwo().then(res => {
   stepThree().then(res => {})
  })
 })
 .catch(err => {
  console.log(err)
 })
}

上面的代码示例中,try...catch...和Promise.catch用于统一捕获错误,在错误抛出后采用冒泡机制。 暂时不说这两种形式的优缺点。 想想我们的目的。 假设我们想要识别这些错误,并对不同异步操作的错误进行不同的处理。 这种情况下,简单的使用一个try...catch...氛围,否则Promise.then就无法实现。

最容易想到的解决方案是使用多个try...catch...来嵌套。 然而,有没有更优雅的方式呢?

看了网上的一些解决方案,总结了以下几种方法。 它们之间有一定的相似之处。 我从简单的开始一一介绍。

v1.0 等待 doSomething().catch(e=>e)

creator异步加载_cocos2dx异步加载_cocos-js 异步 个别加载失败

async function runAsync() {
 const res1 = await stepOne().catch(e => e)
 // 对res1的类型或值进行判断
 if(...) {
  // 如果出错
  // 错误处理逻辑
 }
 const res2 = await stepTwo().catch(e => e)
 // 对res2的类型或值进行判断
 if(...) {
  // 如果出错
  // 错误处理逻辑
 }
}

这个方法非常简单。 使用 Promise.catch 方法捕获错误并返回具有已解决状态和错误值的 Promise。

如果发生错误,则res1的值为error,所以这里一般使用条件语句来判断res1,然后再继续。

v1.1先报错

采用先返回错误的原则,这里的写法和Node.js、Golang是一样的。 也是之前写法的增强版,将值和错误数组一起返回。

async function runAsync() {
 const [err1, res1] = await stepOne(val => [null, val]).catch(e => [e, null])
 if(err1) {
  // 如果出错
  // 错误处理逻辑
 }
 const [err2, res2] = await stepTwo(val => [null, val]).catch(e => [e, null])
 if(err2) {
  // 如果出错
  // 错误处理逻辑
 }
}

cocos-js 异步 个别加载失败_creator异步加载_cocos2dx异步加载

这种写法比较实用。 如果我们的项目中只有一两个地方有这样的二次异步操作,需要进行错误处理就足够了。 当然技能特效,我们很快就会发现这种做法带来了大量的重复,并且不符合DRY原则。 例如:

在v2版本中,我们会进行适当的封装~

v1.2 处理catch中的错误并继续抛出

该方法适用于存在错误处理逻辑,并且当前函数的执行会被终止(返回)的场景。 与v1.1中的条件语句类似,处理错误并使用return。

async function runAsync() {
 try{
 const res1 = await stepOne().catch(err => {
  // 处理错误1
  // handleErr(err)
  // 继续抛出错误
  Promise.reject(err)
 })
 const res2 = await stepTwo().catch(err => {
  // 处理错误2
  // handleErr(err)
  // 继续抛出错误
  Promise.reject(err)
 })
 }catch(err) {
  // 统一的错误处理逻辑
 }
}

这里通过调用Promise.reject静态方法,可以继续抛出异步操作的错误,从而不执行后续逻辑,类似于if(err)return。 当然,这样使用typescript时,res1的返回值类型需要进行一些处理。

cocos-js 异步 个别加载失败_cocos2dx异步加载_creator异步加载

v2.0 抽象了try…catch…的逻辑

如果项目中存在很多上述场景,那么就需要进行适当的封装。 否则,我们只是笨拙地将嵌套的try...catch...转换成额外的条件判断+return语句。

首先想到的是将其提取为函数。

const handle = (fn: (...args: any[]) => Promise<{}>) => async (...argsany[]) => {
  try {
    return [nullawait fn(...args)];
  } catch(e) {
    console.log(e, 'e.messagee');
    return [e];
  }
}
async function runAsync() {
 const [err1, res1] = handle(stepOne)
 if(err1) {
  // 错误处理
  // return
 }
 const res2 = handle(steoTwo)
}

上面的代码显示了句柄函数的简单版本。 您可以根据需要使用Error-first规则来封装handle函数。 调用异步操作时,调用句柄对其进行包装。

但实际上,这种封装是比较无用的,因为handle函数的catch子句中只有统一的错误处理逻辑。 如果需要针对性处理,就避免不了在主函数中进行二次判断和处理。

cocos-js 异步 个别加载失败_cocos2dx异步加载_creator异步加载

v2.1 自定义错误类型

这个思路参考了这篇文章

通过自定义错误类型+封装错误处理程序,解决上一个版本中handle函数无法识别错误类型的问题。 当然,这里的实现涉及到高阶函数以及继承Error来构造自定义的错误对象,可能会稍微复杂一些。

不过,这里的错误类型扩展方式还是值得学习的。


class DbError extends Error {
  public errmsgstring;
  public errnonumber;
  constructor(msg: string, code: number) {
    super(msg);
    this.errmsg = msg || 'db_error_msg';
    this.errno = code || 20010;
  }
}
class ValidatedError extends Error {
  public errmsgstring;
  public errnonumber;
  constructor(msg: string, code: number) {
    super(msg);
    this.errmsg = msg || 'validated_error_msg';
    this.errno = code || 20010;
  }
}

在此基础上,作者还扩展了使用装饰器来简化高阶函数逻辑的方法。 有兴趣的朋友可以进一步探索~

cocos2dx异步加载_creator异步加载_cocos-js 异步 个别加载失败

v2.2 将错误处理逻辑提取到装饰器/加载器中

我们刚才所做的就是将错误处理逻辑提取到一个函数中。 还有其他对代码干扰较小的方法,例如将其提取到加载程序中。 下面作者结合babel进行解析和插入。 主要思想是在遍历语法树进行处理时遇到的await表达式的节点前插入try...catch...代码块。链接如下

异步错误处理加载器

这种方式提高了代码编写风格的统一性,减少了侵入性,但同时也降低了灵活性。 而且,在实现此类加载器时cocos-js 异步 个别加载失败,需要针对不同的上下文做出更多的兼容性考虑。

所以我们还是需要根据具体业务场景具体分析。

最后总结一下。

从简单的处理方式到封装得更好的解决方案,其实还是那句话,一定要结合业务场景来看待。 没有最优解,有时甚至是简洁的写法try...catch...嵌套(少于三个)。

文章来源:https://juejin.cn/post/7105682489875628039