Week6 - 原来我Server错误处理本身的方案就是个错误(/゚Д゚)/ - 错误处理篇 [NodeJs转Golan

大家好,因为上次的Golang核心处理的文章需要study的部分非常多,所以我还得再花些时间了解,再请大家见谅((´д`)),本週会先介绍Golang与Node.js错误处理可以讨论与借鉴的地方


你是否有写过以下的code

try {    await request()} catch (error) {    // 处理request的error程序...}

在Node.js里面使用try catch可以妥善的保护程式码,让request()这个非同步函式出错时可以直接跳到catch处执行例外处理,那问题来了,如果现在有多个request()呢?

try {    await request1()    await request2()    await request2()} catch (error) {    // 处理request的error程序...}

这时候发生了错误,当log印出

我因为timeout产生error而爆炸了

我们会发现一件事

到底是哪个request timeout了

三个request回传的error都有可能是timeout,是的,我们的确可以从stack log看出是哪个错误,但在对应处理上我们无法区分,有些人会直接回传timeout message,但这message就有三种可能性,这样的处理方案有时候就像提供错误资讯般。

小智你别闹了,这到底该如何解决

我这边依序列出我一路解决的方法:

一开始,我透过多个try catch来捕捉
try {    await request1()catch (error) {    // 处理request1的error程序...}try {    await request2()catch (error) {    // 处理request2的error程序...}try {    await request3()catch (error) {    // 处理request3的error程序...}

这的确解决了我的问题,我可以精準的对不同request进行错误处理,但这程式码不觉得很丑吗?这样的使用似乎跟callback hell的难阅读有几分相似,非常的难阅读。

我还是用回原本的方法,并自订这些error
async request1() {    try {        // request的程序...    } catch (error) {        if (error.name === 'TypeError' && error.message === 'Failed to fetch') {            throw new customError1('TypeError')        }        else if (error.name === '其他error name' && error.message === '其他error message') {            throw new customError1('其他error')        }        // ...许多的else if    }}// request2, request3也都是各抛出自己的自订error
try {    await request1() // 出错会抛出customError1    await request2() // 出错会抛出customError2    await request2() // 出错会抛出customError3} catch (error) {    if (error instanceof customError1) {        if (error.name === 'TypeError') {            // ...处理request1的`TypeError`程序        }        else if (error.name === '其他error name') {            // ...处理request1的`其他error name`程序        }            }    else if (error instanceof customError2) {        // ...处理request2的error程序    }    else if (error instanceof customError2) {        // ...处理request3的error程序    }}

这解决的难阅读的问题,但又引来了另个问题,就是除了我要自订这些error以外,我在request里面也得分析他原始的error,并且重新赋予给自订error,这其实有点麻烦,因为我的程式码会看到满满的自订error

好吧,那不自订error,我们透过error赋值
const result1 = await request1().catch(error => error)if (result1 instanceof Error) {    // 处理request1的error程序...}const result2 = await request2().catch(error => error)if (result2 instanceof Error) {    // 处理request2的error程序...}const result3 = await request3().catch(error => error)if (result3 instanceof Error) {    // 处理request3的error程序...}

好,我们可以不再自订那么多error了,但现在result有「成功与失败」两种逻辑,这很恼人,这导致了变数的职责很不单一,这个result实际上应该取名resultOrError,但这让一切变得很奇怪

想破头,直到看到Golang的做法

Golang的错误处理中,鼓励大家不使用try catch来处理,而是「好好定义会回传什么错误」

resp, err := http.Get("http://example.com/")if err != nil {// handle error}

原来可以这样,我们把这方法拿回Node.js

async request1() {    try {        // request的程序...        return [result, null]    } catch (error) {        // ..处理error的程序        return [null, finalError]    }}// request2, request3也都是回传阵列
const [result1, error1] = await request1()if (error1) {    // 处理request1的error程序...}const [result2, error2] = await request2()if (error2) {    // 处理request2的error程序...}const [result3, error3] = await request3()if (error3) {    // 处理request3的error程序...}

这样做可以很直接的解决在多个requests之下,出现了什么错误必须要怎么处理的事情,而回传值的职责也更为分明,并且也相当简单。

我目前很喜欢这样的做法,坦白说,这样的做法很类似于以前的callback:

// callbackfunction(err, result) {  if (error) {    //do error handle  }  // do something}

我们单纯把callback hell解决,并且也能精确定位error,而github上也有人喜爱此作法,并且把这做法写成了一个await-to-js套件,大家可以下载使用

来探讨一下

不过,这与许多的语言不同,而这边也有几个有趣的例子可以拿来讨论。

Stack Overflow创始者的Joel Spolsky大神认为,使用try catch无疑会造成程式码的一种「跳跃」,这使得我们要对这些例外处理要处理特定状况变得很困难。

而这种跳跃的意思就是类似于"goto",在try範围内对外发送requests时,如果出现了错误,那会直接跳到catch的範围,如果你不说好这些跳到catch範围的error到底是什么,那你就会面临跟goto一样的状况,「哇赛,现在程式码进到goto到底是为什么」

而在与朋友讨论时,曾被问到:「重构一书的Martin Fowler大神在书中提到,程式码应该要可以逐行了解意图,我想问的是,使用Golang这样的方法是不是把「正向的逻辑」与「例外的逻辑」写在了同一个段落呢?」

我个人认为,这是关于设计者怎么去看待这个例外,

有些人认为:

既然是例外了,那本身就属于不正常的事,那就统一处理这类的例外。

有些人认为:

虽然他是一个程序上的例外,但他存在我整体的流程中,所以也是正向逻辑的一环。

而已我来说,我偏向于后者,虽然我不到经验老到,但我碰到许多的状况是

同种例外我需要做不同的处理

正如文章开头timeout的例子,不同request的timeout其实对我整个程序的流程是需要有不同的处理的,

所以与其说「把例外写在正向逻辑里会导致逻辑不清楚」,不如说「如果能够配合更好的逻辑分层,把例外处理算为正向逻辑的一环,其实更能好好的处理逻辑」

大家是怎么去看待错误逻辑的?我的想法是否与你的想法不同?欢迎大家讨论,谢谢~

参考

良葛格-重构错误处理程式客观对比Node 与 GolangGo的error与panichow-to-write-async-await-without-try-catch-blocks-in-javascript

关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章