处理 Javascript/Typescript 中的错误:自定义异常和结果

处理 javascript/typescript 中的错误:自定义异常和结果

诸如调用 api 或验证用户输入的数据之类的操作在开发中非常常见,并且是可以给出正确结果或失败的函数的示例。一般来说,为了在 javascript(和其他语言)中控制它,我们通常使用并创建简单的异常。

它们似乎是控制我们正在开发的应用程序或程序可能出现的错误的最简单方法。然而,随着项目和团队的成长,开始出现需要我们做更多事情的场景。例如,在大型团队中,如果功能在指示它们是否可能失败时是明确的,那么这将有很大帮助,以便我们的同事能够预测和管理这些错误。

明确操作可能出现的“错误”类型不仅有助于使开发变得更加容易。它还将作为业务规则的文档。

在 javascript 中,我们有一些技术来实现这一点。为了超越理论,让我们考虑一个现实生活中的例子:在酒店预订应用程序中,用户预订房间,收到代码,然后必须付款。付款时,api 目前可以向我们展示以下 3 种“错误”场景:

el código de reserva no existe.el pago es rechazado.el código de reserva ya no es válido.

当我们为用户制作应用程序时,除了这两种情况外,您还应该考虑额外的一种情况:

立即学习“Java免费学习笔记(深入)”;

no hay conexión a internet. (o el servicio no esta disponible)

可以从应用程序的不同组件调用此函数,如果失败,必须向用户显示错误。

考虑到这个例子,让我们回顾一下如何处理它的一些可能性

自定义错误

异常在许多语言中都很常见,javascript 包含一些预定义的异常(例如 syntaxerror)。在处理可能的错误时,一个好的做法是具体化并个性化它们。

在 js 中创建异常只需使用保留字 throw 后跟我们想要的任何内容(如果你是这么读的话)。

function makeerror() {  throw "error string"}

从这个意义上说,js 是非常宽容的,但是抛出不是 js 中的 error 类的后代的东西被认为是不好的做法。

class myerror extends error {  constructor(message) {    super(message);    this.name = "myerror";  }}function makeerror() {  throw myerror("")}

如您所见,错误类带有一个属性,它允许我们更详细地描述为什么我们要创建异常(并且我们可以添加我们想要的属性)。

回到我们举的例子的问题。通过应用自定义错误,我们可以控制在每种情况下要做什么。

class codeinvalid extends error {/*...*/}class paymentrejected extends error {/*...*/}class codeexpired extends error {/*...*/}class rederror extends error {/*...*/}async function apitransaction(code) {  try {     const r = await fetch(/* url */)     const response = await r.json()     ....     if (response.message === "code invalid") {         throw new codeinvalid()     }     // agregar más casos     return response  } catch (e) {    throw new rederror()  }}async function payreservation(code) {  try {     const paydata = await apitransaction(code)     showresulttouser(paydata)     return  } catch (e) {    if (e instanceof codeinvalid) {      showmessagetocodeinvalid()      return    } else if (e instanceof paymentrejected) {      // ...    } else if (e instanceof codeexpired) {      // ...         } else (e instanceof rederror) {      // ...    }     throw e  }}

通过这个,我们不仅能够以不同的方式路由流程,而且还可以区分系统的内部错误(例如,我们在 payreservation 等中使用的某些内部依赖项的错误)与代表业务规则的错误。

这是一个非常好的选择,它满足了我们根据每种情况控制流程的目标,如果有人看到这个函数,他们就知道为什么它会失败。这样我们已经收获很多,但是使用这种方法我们必须考虑一些事情。

如果函数的异常未在 catch 内控制,则会进入“更高级别”。举个例子,如果你有函数 a,它调用 b,这又调用 c 并且 c 抛出异常. 受控制的这将转到 b,如果 b 不控制它,则继续直到 a 等。根据您的情况,这可能是个好消息。通过业务规则声明可能的错误最终可能会很乏味,因为必须检查所有功能是否存在可能的异常。

另一点需要考虑的是今天非常重视的开发者到期。尽管像 jsdoc 这样的工具允许您描述添加方法可能有异常,但编辑器无法识别它。另一方面,typescript 在编写或调用函数时无法识别这些异常。

[] **性能:* 抛出和处理异常对性能有(最小)影响(类似于使用 break)。尽管在像应用程序这样的环境中,影响几乎为零。

使用值 result(或 either)封装错误

如果我们看一下前面的案例,我们创建的异常并不是由于“无法修复”的错误,而是业务规则的一部分。当异常变得普遍时,它们就不再是真正的异常情况,而是为此而设计的。我们可以将“成功”和“错误”状态封装在单个对象中,而不是抛出异常,如下所示。

const responsemodel = {  isok: true,  data: {},  errorname: ''}

如果我们使用 typescript(或 d.ts 使用 jsdoc),我们可以像这样定义类型。

type response = okresponse | errorresponseinterface okresponse { isok: true, data: t}interface errorresponse {  isok: false,  errorname: n}

将其应用到我们的示例中。如果现在我们的 payreservation 函数返回此对象而不是异常,则使用 jsdoc 或 typescript 我们可以指定可以采用的结果类型(从现在开始,为了简单起见,我将把示例放在 typescript 中)。
这有助于团队提前了解函数可能返回哪些错误。

type apitransactionresponse = responsefunction apitransaction(code: number) : promise {  //...}async function payreservation(code) {   const paydata = await apitransaction(code)  if (paydata.isok) {     showresulttouser(paydata)  } else if (paydata.errorname === 'codeinvalid') {    showmessagetocodeinvalid()    return  } else if (paydata.errorname === 'paymentrejected') {    // ...  } else if (paydata.errorname === 'codeinvalid') {    // ...       } else (paydata.errorname === 'rederror') {    // ...  }}

通过应用此方法,我们获得了该方法(有例外情况)的优点,并且在开发时,编辑器将显示有关可能发生的不同“错误”情况的信息。

事实上,这种类型的概念在编程中已经存在很长时间了,在许多函数式语言中他们没有例外,他们使用这种类型的数据来处理错误,今天许多语言都实现了它。例如,在 rust 和 dart 中,result 类本身就存在,kotlin arrow 库也添加了它。

关于如何使用和实现结果有一定的标准,这样我们的代码对于新开发人员来说更容易理解,我们可以依赖这些约定。

结果可以专门表示成功或错误状态(不能同时是两种状态),并且允许在不抛出异常的情况下使用这两种状态。

class result () {  #isok = true  get value() {    return this.#value  }  get error() {    return this.#error  }  get isok() {    return this.#isok  }  get iserror() {    return this.#iserror  }  constructor(value, isok : boolean) {    if ( isok) {      this.#value = value    } else {      this.#error = value    }    this.#isok = isok  }  static ok(value: t) : result {    return new result(value, true)  }  static error(error: e) : result {    return new result(value, false)  }}

该示例使用类,但不是必需的,还有更简单的实现,我通常将一个实现带到我认为可能需要它的项目中,我留下链接以防您想查看它和/或使用它。

如果我们就这样保留它,那么相对于我们之前创建的对象,我们就不会获得太多收益。这就是为什么很高兴知道它通常实现更多方法

例如,在发生错误时返回默认值的 getorelse 方法。

class result () {  //...  function getorelse(defvalue: r) {    if (this.isok) {      return this.#value    }    return defvalue  }  //...}

并折叠以功能性地处理成功/失败流程。

class result () {  //...  function fold(fnok: (arg: r) => any, fnerror: (arg: e) => any) {    if (this.isok) {      return fnok(this.value)    }    return fnerror(this.error)  }  //...}

您还可以使用 either 找到有关错误处理的信息。结果将是具有更大上下文的 either,either 的值为右 () 和左 ()。正如英语中的 right 也用来表示某件事是正确的,它通常具有正确的值,而错误在左侧,但情况不一定如此,结果相对于这是正确值和错误值。

将其应用到我们的示例中,payreservation 看起来像这样:

type apitransactionresponse = resultfunction apitransaction(code: number) : promise {  //...  return new result.ok(paymentdata) // or ...  return new result.error(descriptionerror)}async function payreservation(code) {   const paydataresult = await apitransaction(code)  return paydataresult.fold(    (paydata) => { return showresulttouser(paydata) },    (e) => {      if (e === 'codeinvalid') {        return showmessagetocodeinvalid()      } else if (e === 'paymentrejected') {        //...      } else if (e === 'codeexpired') {        //...      } else if (e === 'rederror') {        //...      }     }  )}

[*] 一个好的做法是为错误建立一个基本数据类型,在示例中使用字符串,但理想情况下它应该有一个更定义的形式,例如可以添加更多数据的命名对象,例如,你可以在这里看到一个例子

乍一看,添加类似乎比其他任何事情都更加过度设计。但结果是一个广泛使用的概念,维护约定可以帮助您的团队更快地捕获它,并且是加强错误处理的有效方法。

使用此选项,我们明确描述函数可能出现的“错误”,我们可以根据错误类型控制应用程序的流程,我们在调用函数时从编辑器获得帮助,最后我们留下错误异常系统中的案例。

尽管有这些优点,但在实施之前还必须考虑一些要点。正如我在本节开头提到的,result 在许多语言中都是原生的,但在 js 中却不是,因此通过实现它,我们添加了一个额外的抽象。另一点需要考虑的是我们所处的场景,并非所有应用程序都需要如此多的控制(例如,在广告活动的登陆页面上,我看不到实现结果的意义)。是否可以充分利用所有潜力是值得评估的,否则只是额外的负担。

简而言之,处理错误不仅可以提高代码质量,还可以通过提供可预测且记录良好的工作流程来提高团队协作。结果和自定义异常是工具,如果使用得当,有助于提高代码的可维护性和健壮性。

ts 中的额外内容

在 typescript 中,我们可以从 result 中获得额外的好处,以确保涵盖所有错误情况:

function typeCheck(_:never) {}async function payReservation(code) {   const payDataResult = await apiTransaction(code)  return payDataResult.fold(    (payData) => { return showResultToUser(payData) },    (e) => {      if (e === 'CodeInvalid') {        return showMessageToPartnerError()      } elseif (e === 'PaymentRejected') {        //...      } elseif (e === 'CodeExpired') {        //...      } elseif (e === 'RedError') {        //...      }  else {        typeCheck(e)      }    }  )}

typecheck 函数旨在验证 if/else if 中检查了 e 的所有可能值。
在此存储库中,我留下了更多细节。

以上就是处理 Javascript/Typescript 中的错误:自定义异常和结果的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1498610.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月19日 20:36:21
下一篇 2025年12月19日 20:36:31

相关推荐

  • js如何实现分页功能

    js实现分页的核心是计算总页数并截取对应数据展示,1. 首先准备数据数组alldata,设定每页数量pagesize和当前页currentpage;2. 通过math.ceil(alldata.length / pagesize)计算总页数totalpages;3. 利用slice()方法截取(st…

    2025年12月20日
    000
  • JS如何实现发布订阅模式

    发布订阅模式通过中间调度中心解耦发布者与订阅者,1. 需实现eventemitter类包含subscribe、publish和unsubscribe方法;2. 在react中可通过context api共享事件总线实例;3. 组件使用useeffect订阅并在卸载时取消以避免内存泄漏;4. 与观察者…

    2025年12月20日
    000
  • 什么是主席树?主席树的可持久化

    主席树通过共享节点实现可持久化,支持查询历史版本,空间复杂度O(N log N),常用于静态区间第K大问题,其核心是在修改时仅新建必要节点,其余指向旧版本,从而高效保存多版本线段树。 主席树,又称可持久化线段树,本质上是一种可以查询历史版本线段树的数据结构。它通过共享线段树的节点,大幅降低了空间复杂…

    2025年12月20日
    000
  • js 如何格式化日期字符串

    javascript格式化日期字符串的核心是将date对象按需转换为指定格式,如”yyyy-mm-dd”或”mm/dd/yyyy hh:mm:ss”。最直接的方法是使用tolocaledatestring()和tolocaletimestring(),…

    2025年12月20日
    000
  • JS如何实现依赖注入?DI容器的实现

    答案:JavaScript实现依赖注入的核心是通过DI容器解耦组件与其依赖,提升可测试性、可维护性和模块独立性。容器通过register注册依赖,resolve递归解析并注入依赖,支持构造函数注入等模式,适用于中大型项目以集中管理复杂依赖,但需权衡学习成本与实际需求,避免过度设计。 JavaScri…

    2025年12月20日
    000
  • js 如何实现无限滚动

    传统的“加载更多”按钮会打断用户浏览的流畅性,迫使用户从内容消费中抽离进行操作,破坏沉浸感,尤其在移动端体验较差;2. 优化无限滚动性能需采用节流控制滚动事件频率、使用documentfragment减少dom操作、实施图片懒加载、优化后端响应,并在数据量大时引入列表虚拟化技术;3. 无限滚动不适用…

    2025年12月20日
    000
  • js怎样实现倒计时功能

    倒计时功能的核心是计算目标时间与当前时间的差值并实时更新显示,1. 获取目标时间需使用new date()创建日期对象,可基于utc避免时区偏差;2. 计算时间差通过gettime()获取毫秒数并转换为天、时、分、秒;3. 格式化显示使用padstart确保两位数展示;4. 使用setinterva…

    2025年12月20日 好文分享
    000
  • js怎样实现分页功能

    客户端分页适用于数据量较小(如几百到几千条)的场景,所有数据预先加载到浏览器,通过javascript切分显示,切换页面无网络延迟,适合数据变动少、追求流畅体验的内部系统或小型页面;2. 服务器端分页适用于大数据量(如成千上万条)的场景,每次请求只获取当前页数据,减轻浏览器负担,确保性能和可扩展性,…

    2025年12月20日 好文分享
    000
  • JS如何实现this绑定?this的指向规则

    JavaScript中this的指向遵循五种核心规则:1. new绑定优先级最高,this指向新创建的实例;2. 显式绑定通过call、apply或bind方法强制指定this值;3. 隐式绑定发生在对象方法调用时,this指向调用该方法的对象;4. 箭头函数采用词法绑定,this继承外层作用域的t…

    2025年12月20日
    000
  • 哈希算法是什么?常见哈希函数介绍

    哈希算法是数据安全的基石,因其单向性、抗碰撞性和雪崩效应,广泛用于数据完整性校验、密码存储、数字签名和区块链。它通过固定长度哈希值确保信息不可篡改,即使输入微小变化也会导致输出巨大差异。MD5和SHA-1因碰撞漏洞已不安全,SHA-2(如SHA-256)成为主流,广泛用于区块链和SSL/TLS;SH…

    2025年12月20日
    000
  • javascript如何实现数组并发处理

    javascript中实现数组并发处理的核心是通过异步编程与任务调度提升数据处理效率。1. 使用promise.all()可并发执行所有任务,但任一失败则整体失败;2. promise.allsettled()确保所有任务完成,无论成功或失败,适合需收集全部结果的场景;3. 通过任务队列手动控制并发…

    2025年12月20日 好文分享
    000
  • javascript闭包怎样实现策略模式

    闭包实现策略模式的核心在于其能封装私有状态并返回可复用的函数,使策略具有独立上下文;2. 其优势包括极致的封装性、灵活的参数化、避免this指向问题及便于测试;3. 实际挑战包括调试困难、潜在内存泄漏和团队理解成本,可通过保持策略简洁、管理引用和加强文档来规避;4. 闭包还可应用于模块模式、单例模式…

    2025年12月20日 好文分享
    000
  • js中如何实现路由跳转

    在javascript中实现路由跳转的核心是通过hash模式或history模式在不刷新页面的前提下改变url并动态渲染内容。1. hash模式利用url中#后的哈希值变化触发hashchange事件,兼容性好且无需服务器配置,但url不美观且不利于seo;2. history模式使用html5的p…

    2025年12月20日 好文分享
    000
  • 什么是约瑟夫问题?JS如何解决约瑟夫问题

    约瑟夫问题的核心逻辑是:在一个环形结构中按固定步长循环计数并逐个淘汰,直到剩下最后一个人;在javascript中,使用数组模拟虽直观但性能较差,因为splice操作的时间复杂度为o(n),导致整体复杂度达o(n²);而更高效的数学解法基于递推公式f(n, k) = (f(n-1, k) + k) …

    2025年12月20日
    000
  • javascript闭包怎样延长变量生命周期

    闭包能延长变量生命周期,因为它使内部函数持续引用外部函数作用域中的变量,从而阻止垃圾回收机制回收这些变量;2. 其原理基于javascript的词法作用域和垃圾回收机制,闭包会捕获并保持对外部词法环境的引用,只要闭包存在,被引用的变量就一直存活;3. 常见应用场景包括模块模式、私有变量创建、函数工厂…

    2025年12月20日 好文分享
    000
  • JS如何实现内存管理?垃圾回收机制

    JavaScript通过自动内存管理和垃圾回收机制避免内存泄漏,核心是标记-清除算法与分代回收策略,结合Chrome DevTools的堆快照和时间线分析可有效诊断内存问题。 JavaScript的内存管理和垃圾回收机制,说白了,就是浏览器引擎(比如V8)在幕后默默地帮我们处理内存的分配与释放,这样…

    2025年12月20日
    000
  • 什么是Source Map?源码映射的应用

    Source Map是前端调试的基石,它将压缩混淆后的代码映射回原始源码,使开发者能在浏览器中直接调试TypeScript或ES6+代码;通过构建工具生成,支持错误堆栈还原,提升生产环境bug定位效率;需注意生产环境安全,避免源码泄露,常用hidden-source-map并配合Sentry等平台使…

    2025年12月20日
    000
  • js 怎样实现打印功能

    最直接的打印方式是调用 window.print() 方法,它会触发浏览器打印对话框并打印当前页面全部内容;2. 若需打印特定区域,推荐使用隐藏的 iframe 方式:创建一个隐藏 iframe,将目标内容及样式复制进去,调用其 contentwindow.print(),避免影响主页面;3. 精确…

    2025年12月20日
    000
  • javascript闭包怎么在异步操作中保留值

    闭包能保留值是因为函数会记住其创建时的词法作用域,即使外部函数已执行完毕,内部函数仍可通过闭包访问并保持对当时变量的引用。1. 在异步操作中,由于javascript是单线程并依赖事件循环,回调函数往往在外部变量已变化后才执行,导致访问到的是最新值而非预期值;2. 使用闭包可通过iife为每个回调创…

    2025年12月20日 好文分享
    000
  • 解决Angular中ngOnInit无法响应动态输入更新API链接的问题

    本文探讨了Angular应用中,当组件的@Input属性动态更新时,ngOnInit为何无法重新触发API调用以更新链接的问题。我们将深入分析Angular的生命周期钩子,并提供两种解决方案:一是采用服务层分离API逻辑的最佳实践,通过父组件管理数据流并使用async管道,使子组件成为“哑组件”;二…

    好文分享 2025年12月20日
    000

发表回复

登录后才能评论
关注微信