你的错误处理一团糟-是时候修复它了-️

news/2025/10/21 23:18:11/文章来源:https://www.cnblogs.com/ltpp/p/19156653

GitHub 主页

你的错误处理一团糟,是时候修复它了!🛠️

我还记得那个让我彻夜难眠的 bug。一个支付回调接口,在处理一个罕见的、来自第三方支付网关的异常状态码时,一个Promise链中的.catch()被无意中遗漏了。结果呢?没有日志,没有警报,服务本身也没有崩溃。它只是“沉默地”失败了。那个用户的订单状态永远停留在了“处理中”,而我们,对此一无所知。直到一周后,在对账时我们才发现,有数百个这样的“沉默订单”,造成了数万美元的损失。💸

这个教训是惨痛的。它让我明白,在软件工程中,我们花在处理成功路径上的时间,可能还不到 10%。剩下 90%的复杂性,都来自于如何优雅、健壮地处理各种预料之中和意料之外的错误。 而一个框架的优劣,很大程度上就体现在它如何引导我们去面对这个“错误的世界”。

很多框架,尤其是那些动态语言的“灵活”框架,它们在错误处理上的哲学,几乎可以说是“放任自流”。它们给了你一万种犯错的可能,却只给了你一种需要极度自律才能做对的方式。

回调地狱与被吞噬的Promise:JavaScript 的错误处理之殇

在 Node.js 的世界里,我们经历了一场漫长的、与错误作斗争的进化史。

阶段一:回调地狱 (Callback Hell)

老一辈的 Node.js 开发者都还记得被“金字塔”支配的恐惧。

function processOrder(orderId, callback) {db.findOrder(orderId, (err, order) => {if (err) {// 错误处理点 1return callback(err);}payment.process(order, (err, result) => {if (err) {// 错误处理点 2return callback(err);}inventory.update(order.items, (err, status) => {if (err) {// 错误处理点 3return callback(err);}callback(null, status); // 成功!});});});
}

这种“错误优先”的回调风格,在理论上是可行的。但随着业务逻辑的复杂化,代码会向右无限延伸,形成一个难以维护的“死亡金字塔”。你必须在每一个回调里,都记得去检查那个err对象。只要有一次疏忽,错误就会被“吞掉”。

阶段二:Promise的救赎与新的陷阱

Promise的出现,把我们从回调地狱中解救了出来。我们可以用.then().catch()来构建一个更扁平、更易读的异步链。

function processOrder(orderId) {return db.findOrder(orderId).then((order) => payment.process(order)).then((result) => inventory.update(result.items)).catch((err) => {// 统一的错误处理点console.error('Order processing failed:', err);// 但这里,你必须记得向上抛出错误,否则调用者会认为成功了throw err;});
}

这好多了!但新的问题又来了。如果你在一个.then()里忘记了return下一个Promise,或者在一个.catch()里忘记了重新throw错误,这个链条就会以一种你意想不到的方式继续执行下去。错误,再一次被“沉默地”吞噬了。

阶段三:async/await的优雅与最后的伪装

async/await让我们能用看似同步的方式来编写异步代码,这简直是天赐的礼物。

async function processOrder(orderId) {try {const order = await db.findOrder(orderId);const result = await payment.process(order);const status = await inventory.update(result.items);return status;} catch (err) {console.error('Order processing failed:', err);throw err;}
}

这看起来已经很完美了,不是吗?但它依然依赖于程序员的“自觉”。你必须记得把所有可能出错的异步调用都包在一个try...catch块里。如果你忘了await一个返回Promise的函数呢?那个函数里的错误将永远不会被这个try...catch捕获。

JavaScript 的问题在于,错误是一个可以被轻易忽略的值nullundefined可以像幽灵一样在你的代码里游荡。你需要依靠严格的规范、Linter 工具和个人纪律,才能确保每一个错误都被正确处理。而这,恰恰是不可靠的。

Result枚举:当编译器成为你最可靠的错误处理伙伴

现在,让我们进入 Rust 和 hyperlane 的世界。在这里,错误处理的哲学是完全不同的。Rust 语言的核心,有一个叫做Result<T, E>的枚举类型。

enum Result<T, E> {Ok(T),  // 代表成功,并包含一个值Err(E), // 代表失败,并包含一个错误
}

这个设计,简单而又深刻。它意味着一个可能失败的函数,它的返回值必然是这两种状态之一。它不再是一个可能为null的值,或者一个需要你在别处.catch()Promise。它是一个完整的、包含了所有可能性的类型。

最关键的是,编译器会强制你处理Err的情况。如果你调用一个返回Result的函数,却不处理它的Err分支,编译器会直接给你一个警告甚至错误。你不可能“不小心”忽略一个错误。

让我们看看在 hyperlaneservice 层,代码会是什么样子:

// 在一个 service 文件中
pub fn process_order(order_id: &str) -> Result<Status, OrderError> {let order = db::find_order(order_id)?; // `?` 操作符:如果失败,立即返回Errlet result = payment::process(order)?;let status = inventory::update(result.items)?;Ok(status) // 明确返回成功
}

看到那个?操作符了吗?它是 Rust 错误处理的精髓。它相当于在说:“调用这个函数,如果它返回Ok(value),就把value取出来继续执行;如果它返回Err(error),就立刻从当前函数返回这个Err(error)。”

这种模式,把之前 JavaScript 中需要try...catch才能实现的逻辑,变成了一种极其简洁、清晰、且由编译器保证安全的链式调用。错误,不再是需要被“捕获”的异常,而是数据流中一个可预期的、被优雅处理的分支。

panic_hook:最后的防线

当然,总有一些错误是我们无法预料的,也就是panic(恐慌)。比如数组越界、整数溢出等。在很多框架中,一个未被捕获的panic会导致整个线程甚至进程崩溃。

hyperlane 提供了一个优雅的“最后防线”——panic_hook。我们在之前的文章中已经见过它的身影:

async fn panic_hook(ctx: Context) {let error: Panic = ctx.try_get_panic().await.unwrap_or_default();let response_body: String = error.to_string();eprintln!("{}", response_body); // 记录详细的错误日志// 向客户端返回一个标准的、安全的 500 错误响应let _ = ctx.set_response_status_code(500).await.set_response_body("Internal Server Error").await.send().await;
}// 在 main 函数中注册它
server.panic_hook(panic_hook).await;

这个钩子能捕获任何在请求处理过程中发生的panic。它能防止服务器直接崩溃,并允许你记录下详细的错误信息用于事后分析,同时给客户端返回一个友好的错误页面,而不是一个断开的连接。这是一种极其负责任和健壮的设计。

别再祈祷代码不出错了,从一开始就拥抱错误

好的错误处理,不是在代码的各个角落里都塞满try...catch。它是从语言和框架层面,就为你提供一套机制,让“失败”成为程序流程中一个可预期的、一等公民。

Rust 的Result枚举强迫你直面每一个可能的失败,而hyperlane的架构和钩子系统则为你提供了处理这些失败的优雅模式。它把错误处理从一种“开发者纪律”,变成了一种“编译器保证”。

所以,如果你还在为那混乱的错误处理逻辑而头痛,为那些“沉默”的失败而恐惧,那么问题可能真的不在于你不够努力,而在于你选择的工具,从一开始就没有把“健壮性”放在最重要的位置。是时候,选择一个能和你并肩作战,共同面对这个不完美世界的伙伴了。

GitHub 主页

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/942760.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[网络] [iproute2] tc: Linux 带宽限制(Token Bucket Filter 和 HTB)使用指南

[网络] [iproute2] tc: Linux 带宽限制(Token Bucket Filter 和 HTB)使用指南$(".postTitle2").removeClass("postTitle2").addClass("singleposttitle");参考:iproute2: 网络管理利…

C++编程练习

// 阿汪面前有两只盲盒,每只盒子打开都有两种可能:或者装了 X 克狗粮,或者是一只容量为 Y 克的狗粮储蓄盒。如果是狗粮,阿汪可以快乐地吃掉;如果是空储蓄盒,那就倒霉了,阿汪必须想办法找到狗粮把这只储蓄盒装满…

newDay14

1.做了几个Java的小练习,背背单词,写了一些英语作业,时间还是不太够 2.明天课就少了,继续往下学 3.自己c++还是太差了,java学得差不多就回去补c++

L07_在RuoYI项目中添加自己的接口并实现CRUD功能(轻松+AI版)

这里是废话部分 从来没有写过如此轻松的作业,上课两个多小时就听进去了几分钟的东西,这几分钟的东西还是操作,其他是半点都没入入脑,靠着这几分钟的东西,轻松的完成了作业的1/2。 在L06中折磨的看了三天的文档,大…

大二to大三暑假大三上前半学期总结

比赛,学习,刚谈上恋爱,可是然后呢?其实心态得到了比较大的改变,比大二下的时候好受了很多,可能是逐渐接收了事实了。 比较大的事情其实也没几个,第一是和同学一起去打了一个超算比赛 是并行应用挑战赛2025,去鄂…

带权拉格朗日中值定理的证明

带权拉格朗日中值定理:设 \(f(x)\) 在 \([a, b]\) 连续,且 \(g(x)\) 在 \([a,b]\) 可积且不变号(恒大于零或恒小于零),那么存在 \(c\in [a,b]\),使得 \[\int^b_af(x)g(x)\text{d}x = f(c)\int^b_ag(x)\text{d}x …

Linux 下将程序打包为安装包

对于 Linux 下的程序打包,个人首推 FPM 构建工具。它比 dpkg、rpmbuild 要好用的多(至少 2021 年那会如此),并且它同时支持 deb、rpm 等包格式。 以下回顾一下以前写过的安装包构建脚本: #!/bin/bash#-----------…

低代码如何推动企业敏捷创新与业务赋能

在创新驱动发展的时代,企业需要更快地响应市场变化,更灵活地调整业务策略。然而,传统的软件开发模式往往无法满足这种敏捷性要求。低代码技术的出现,正在改变这一现状,成为推动企业敏捷创新的重要力量。低代码与敏…

hevc解码器下载

如果你使用的是品牌电脑,那电脑重装系统后会自动下载,无需手动安装,连机械革命都会自动安装的 如果不是,那么直接下载最新版(本文截稿于2025/10/21) Microsoft.HEVCVideoExtensions_2.4.23下载 双击打开即可安装…

低代码如何成为企业数字化转型的加速器

在当今快速变化的商业环境中,企业数字化转型已从"选择题"变成了"必答题"。然而,传统软件开发模式的高成本、长周期,让许多企业在数字化转型的道路上步履维艰。这时,低代码技术应运而生,正以其…

2025.10.18 刷题

2025.10.18 刷题1. P14253 旅行(trip) 一眼,区间肯定到结尾 然后从后面扫,随便统计一下个数即可 2. P14254 分割(divide) 刚看很难 再看诈骗 发现限制是要求同等深度,然后最小的必须有两个 然后这个是个独立问题…

[网络] [iproute2] tc命令:Linux网络异常模拟(Network Emulation, netem)指南

[网络] [iproute2] tc命令:Linux网络异常模拟(Network Emulation, netem)指南$(".postTitle2").removeClass("postTitle2").addClass("singleposttitle");目录01 简介02 前提条件2.1…

删除链表的倒数第N个结点-leetcode

题目描述 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 示例 1:输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]示例 2: 输入:head = [1], n = 1 输出:[]示例 3: 输入:head = [1,2], n =…

NOI 八

根号分治?+吉司机/扫描线/单调栈A. 先考虑 unordered_map 暴力记录的做法,容易发现 LCM 太大了存不下,状态数也不可接受。 LCM 考虑质因数分解,发现 \(V=300\) ,\(>17\) 的质因数只会至多出现一次。 这样的质…

2025.10.21总结

今天继续看软考相关内容,今天复习了计算机网络相关知识,和程序设计语言基础,还有汇编语言,汇编语言太晦涩难懂了,看了一部分,例题跟着过了一下就过了,系统的学的话需要很长时间,这部分以做题为主吧。 目前也还…

10.18测试

T1 Bug 题目描述 A 君在机缘巧合下得到了一把养蛊神器,于是 A 君希望培养出迄今为止战斗力最强的 Bug。A 君把现有的 \(n\) 个 Bug 排成一个序列 \(a_1, a_2, \dots, a_n\),其中 \(a_i\) 表示第 \(i\) 个 Bug 的战斗…

Day1标签的关系与vs的注释

<html><head></head><body></body> </html>标签之间的关系有两种,嵌套与并列关系,也可形象对的理解为父子与兄弟 在上述代码中,head与body都是html的子标,而head与body则是并…

软件工程学习日志2025.10.21

项目概述 在本次开发任务中,我使用Trae框架成功实现了一个功能完整的即时在线聊天软件。该应用支持文字和图片的实时发送与接收,界面简洁美观,充分体现了现代Web应用的设计理念。 技术架构 前端技术栈 • 核心框架:…

[PaperReading] DeepSeek-OCR: Contexts Optical Compression

目录DeepSeek-OCR: Contexts Optical CompressionTL;DRMethodDeepEncoderDeepDecoderDataExperiment总结与思考相关链接 DeepSeek-OCR: Contexts Optical Compression link 时间:25.10.20 单位:DeepSeek 作者相关工作…

Win10安装WindowsCamera相机

在一些很旧的win10非正式版中,相机可能缺失,很老的win10只能用过时的WindowsCamera相机。这里给出一个安装包,当然新系统也可安装 夸克网盘下载 首先,打开开发者模式(如果有),不然无法安装然后右键打开——以管…