呼叫 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.
當我們為使用者製作應用程式時,除了這兩種情況之外,您還應該考慮額外的一種情況:
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("") }
如您所見,錯誤類別帶有一個屬性,它允許我們更詳細地描述為什麼我們要建立異常(並且我們可以添加我們想要的屬性)。
回到我們舉的例子的問題。透過應用自訂錯誤,我們可以控制在每種情況下要做什麼。
El código de reserva no existe. El pago es rechazado. El código de reserva ya no es válido.
透過這個,我們不僅能夠以不同的方式路由流量,而且還能夠區分系統的內部錯誤(例如,我們在 payReservation 等中使用的某些內部依賴項的錯誤)與代表業務規則的錯誤。
這是一個非常好的選擇,它滿足了我們根據每種情況控制流程的目標,如果有人看到這個函數,他們就知道為什麼它會失敗。這樣我們已經收穫很多,但是我們必須考慮這種方法的一些事情。
如果函數的異常未在 catch 內控制,則會進入「更高層級」。舉個例子,如果你有函數A,它調用B,這又調用C 並且C 拋出異常. 受控之後將前往B,如果B不控制它則繼續,直到A等等。根據您的情況,這可能是個好消息。透過業務規則聲明可能的錯誤最終可能會很乏味,因為必須檢查所有功能是否存在可能的異常。
另一點需要考慮的是今天非常重視的開發者到期。儘管像 JsDoc 這樣的工具允許您描述添加方法可能有異常,但編輯器無法識別它。另一方面,Typescript 在編寫或呼叫函數時無法識別這些異常。
[] **效能:* 拋出和處理異常對效能有(最小)影響(類似於使用 break)。儘管在像應用程式這樣的環境中,影響幾乎為零。
如果我們看一下前面的案例,我們創建的異常並不是由於「無法修復」的錯誤,而是業務規則的一部分。當異常變得普遍時,它們就不再是真正的異常情況,而是為此而設計的。我們可以將「成功」和「錯誤」狀態封裝在單一物件中,而不是拋出異常,如下所示。
No hay conexión a internet. (o el servicio no esta disponible)
如果我們使用 typescript(或 d.ts 使用 jsdoc),我們可以像這樣定義類型。
function makeError() { throw "Error string" }
將其應用到我們的範例中。如果現在我們的 payReservation 函數傳回此物件而不是異常,則使用 JSDoc 或 Typescript 我們可以指定我們可以採用的結果類型(從現在開始,為了簡單起見,我將把範例放在 typescript 中)。
這有助於團隊提前了解函數可能傳回哪些錯誤。
class MyError extends Error { constructor(message) { super(message); this.name = "MyError"; } } function makeError() { throw MyError("") }
透過應用此方法,我們獲得了該方法(有例外情況)的優點,而且在開發時,編輯器將顯示有關可能發生的不同「錯誤」情況的資訊。
事實上,這種類型的概念在程式設計中已經存在很長時間了,在許多函數式語言中他們沒有例外,他們使用這種類型的資料來處理錯誤,今天許多語言都實現了它。例如,在 Rust 和 Dart 中,Result 類別本身就存在,Kotlin 的 Arrow 函式庫也加入了它。
關於如何使用和實現結果有一定的標準,這樣我們的程式碼對於新開發人員來說更容易理解,我們可以依賴這些約定。
結果可以專門表示成功或錯誤狀態(不能同時是兩種狀態),並且允許在不拋出異常的情況下使用這兩種狀態。
El código de reserva no existe. El pago es rechazado. El código de reserva ya no es válido.
該示例使用了類,但不是必需的,還有更簡單的實現,我通常將一個實現帶到我認為可能需要它的項目中,我留下鏈接以防您想查看它和/或使用它。
如果我們就這樣保留它,那麼相對於我們之前創建的對象,我們就不會獲得太多收益。這就是為什麼很高興知道它通常實作更多方法
例如,在發生錯誤時傳回預設值的 getOrElse 方法。
No hay conexión a internet. (o el servicio no esta disponible)
並折疊以功能性地處理成功/失敗流程。
function makeError() { throw "Error string" }
您也可以使用 Either 找到有關錯誤處理的資訊。結果將是具有更大上下文的 Either,Either 的值為右 (右) 和左 (左)。正如英語中的right 也用來表示某件事是正確的,它通常具有正確的值,而錯誤在左側,但情況不一定如此,結果相對於這是正確值和錯誤值。
將其應用到我們的範例中,payReservation 看起來像這樣:
class MyError extends Error { constructor(message) { super(message); this.name = "MyError"; } } function makeError() { throw MyError("") }
[*] 一個好的做法是為錯誤建立一個基本資料類型,在範例中使用字串,但理想情況下它應該有一個更定義的形式,例如可以添加更多資料的命名對象,例如,你可以在這裡看到一個例子
乍一看,添加類別似乎比其他任何事情都更加過度設計。但結果是一個廣泛使用的概念,維護約定可以幫助您的團隊更快地捕獲它,並且是加強錯誤處理的有效方法。
使用此選項,我們明確描述函數可能出現的“錯誤”,我們可以根據錯誤類型控制應用程式的流程,我們在調用函數時從編輯器獲得幫助,最後我們留下錯誤異常系統中的案例。
儘管有這些優點,但在實施之前還必須考慮一些要點。正如我在本節開頭提到的,Result 在許多語言中都是原生的,但在 JS 中卻不是,因此透過實作它,我們加入了一個額外的抽象。另一點需要考慮的是我們所處的場景,並非所有應用程式都需要如此多的控制(例如,在廣告活動的登陸頁面上,我看不到實現結果的意義)。值得評估的是您是否可以充分利用所有潛力,否則只會額外增加負擔。
簡而言之,處理錯誤不僅可以提高程式碼品質,還可以透過提供可預測且記錄良好的工作流程來提高團隊協作。結果和自訂異常是工具,如果使用得當,有助於提高程式碼的可維護性和健壯性。
在 TypeScript 中,我們可以從 Result 獲得額外的好處,以確保涵蓋所有錯誤情況:
El código de reserva no existe. El pago es rechazado. El código de reserva ya no es válido.
typeCheck 函數旨在驗證 if/else if 中檢查了 e 的所有可能值。
在此存儲庫中,我留下了更多細節。
以上是處理 Javascript/Typescript 中的錯誤:自訂例外狀況和結果的詳細內容。更多資訊請關注PHP中文網其他相關文章!