詳解JS中異常與錯誤處理的正確方法
簡介
首先,這篇文章一定會引起爭議,因為對于錯誤處理從來就沒有真正的標(biāo)準(zhǔn)答案,每個人都會有自己的主觀意見。 我的理解畢竟也是片面,提出的想法主要是基于個人的經(jīng)驗總結(jié),如果有異議,歡迎交流討論。 為了能夠盡量保持客觀,我會將處理思想盡量前置,再圍繞處理思想展開。 這樣大家就能先思考這個思想是否能夠成為前提,以及在這個思想前提下的最優(yōu)處理方式。
1 面向錯誤編程
為什么說面向錯誤編程,這里就要向大家發(fā)出靈魂一問,你能確保你寫的每行代碼都是正常的嗎? 假設(shè)你的代碼天衣無縫,但你能確保你調(diào)用別人提供的方法也不會出現(xiàn)問題嗎? 顯然,我們無法保證每行代碼的正確性,也無法保證調(diào)用別人的方法一定能成功執(zhí)行。所以如何處理錯誤就成了重中之重。 這時候可能就有人說了:“當(dāng)然有可能會出現(xiàn)問題啊,不然要測試干嘛?”我們需要知道的是,當(dāng)這個錯誤暴露出來的時候, 可能就造成了無法估量的損失。測試人員在進(jìn)行測試的時候是不知道我們的代碼是如何寫的,是很有可能測試時候漏了一些點的, 而這每個遺留點都會成為一個個地雷,說不定哪天就爆了。這也是為什么很多公司都特別重視單元測試的原因。 關(guān)于單元測試我們回頭專門講解,今天主要討論面向錯誤編程。為了防止出現(xiàn)極端情況,我們需要先考慮錯誤情況,再去考慮正常。 這種編程思想我們將其稱之為面向錯誤編程。
1.1 墨菲定律
墨菲定律講的是一個事件如果有兩種或兩種以上的方式去做某件事情,而其中一種方式將導(dǎo)致災(zāi)難,則必定有人會做出這種選擇。 就像一艘船足夠穩(wěn)固,翻船的概率幾乎為零,但是它還是有翻船的可能性,所以就得備好救生衣。 同樣,即使你的代碼出錯概率為百萬分之一,但是那百萬分之一一旦發(fā)生,對使用者來說就是100%,所以必須做好應(yīng)急策略。
1.2 先判否
在代碼出錯這點上,我們可以假設(shè)一個極端情況,即我們寫的每行代碼都有可能出錯。 所以在寫代碼之前我們就需要考慮到可能出現(xiàn)錯誤的情況,在代碼上的體現(xiàn)就是我們可能先寫了一堆錯誤的處理,最后才去處理正確的邏輯。 面向錯誤編程的核心技巧就是先判否。
function doSomeThing (params) { if (!params.a) return if (params.b !== true) return // ... // 開始寫正確代碼邏輯 }
錯誤不是異常
我們需要明確,異常是代碼運行時發(fā)生的異常信息,如果不處理會導(dǎo)致代碼無法運行。而錯誤是代碼在運行過程中一個不期待的結(jié)果。 比如發(fā)起一個 http 請求,即使出現(xiàn)網(wǎng)絡(luò)出錯等情況導(dǎo)致請求失敗,但是請求即使失敗了,也應(yīng)該是不影響代碼繼續(xù)運行的。 對于我們來說一個請求只有一個結(jié)果,要么成功,要么失敗,而失敗其實也是一個結(jié)果,那么這就是一個錯誤。 但是在執(zhí)行 JSON.parse 方法的時候如果解析出錯不做處理,就會因為解析異常而導(dǎo)致代碼運行終止,那么這就是一個異常。
2. js 內(nèi)置的錯誤處理
2.1 Error 類
當(dāng)運行時錯誤產(chǎn)生時,Error 對象會被拋出。Error 對象也可用于用戶自定義的異常的基礎(chǔ)對象。js還封裝了一些內(nèi)置的標(biāo)準(zhǔn)錯誤類型。 如語法錯誤的 SyntaxError,類型錯誤的 TypeError,以及 ReferenceError、RangeError、URIError、AggregateError、AggregateError、InternalError。
2.2 throw
throw 用于拋出一個異常。經(jīng)常結(jié)合 Error 一起使用。
2.3 try catch
try catch 用于捕獲一個可能出現(xiàn)的未知異常。剛剛提到的 JSON.parse 就是一個最好的例子,在解析一個字符串的時候, 你沒法確保字符串就一定是一個 json 字符串,所以必須使用 try catch 包裹異常,不然就會發(fā)生代碼運行終止。 而我們很多同學(xué)在 try catch 的使用過程中經(jīng)常會發(fā)生濫用現(xiàn)象,比如使用 try catch 包裹請求錯誤:
try { const res = await request() if (res) { //... } } catch(err) { // ... // 請求錯誤處理 }
我個人非常反對這種使用,為什么呢,按照剛剛說的,請求結(jié)果其實是一個值,即使請求失敗,返回的也是一個值。 而在 try 里面即使拿到請求成功的值,在后續(xù)處理 res 的過程中如果發(fā)生語法錯誤,這時也會拋出異常, 那此時你還能確定這個 catch 捕獲到的異常是請求失敗了還是處理 res 時候報錯的嗎? 我之前說過一句話,濫用 try catch 和隨地大小便無異。所以希望大家不要再使用這種方法。
2.4 Promise.catch
js 中還提供了內(nèi)置類 Promise,promise 的存在不僅僅是解決了 js 回調(diào)地獄的問題,而且按照 promise 的設(shè)定,promise 必定會返回一個結(jié)果。 這個結(jié)果要么是成功,要么是失敗。這點其實就和剛剛提到的請求結(jié)果非常搭配,比如 axios 請求庫就使用 promise 返回請求結(jié)果。所以針對上面的例子我們應(yīng)該這樣
// request 返回一個 promise request() .then(res => { // 這里處理請求成功 // ... // 如果業(yè)務(wù)代碼可能出錯,再使用 try catch try { // ... } catch (err) { // ... } }) .catch()
3. 錯誤處理只有一次
代碼運行出錯時,我們只處理一次。如果你能立即確定錯誤的處理方案,你就直接處理掉。 當(dāng)你處理不了這個錯誤的時候,就要把詳細(xì)的錯誤結(jié)果包裝好。這樣別人在調(diào)用的時候就能自己去處理錯誤了。
這里我們繼續(xù)使用請求來舉例,我們在請求時, 可能因為 token 過期導(dǎo)致請求 401 導(dǎo)致失敗,你不可能在每個接口調(diào)用的地方都判斷一次是否 401 吧。 這種 401 導(dǎo)致的失敗是在封裝請求方法的時候我們就能處理的,具體的請求不需要再去處理, 那我們就不需要把 401 這種錯誤告訴具體的請求使用函數(shù),自己處理就行了,我們只需要告訴它錯誤了就行。
但是具體的請求業(yè)務(wù)錯誤可能就無法處理了。舉個常見的例子,比如你獲取一個列表數(shù)據(jù),我們在請求時經(jīng)常和后端約定請求成功碼。經(jīng)??吹接型瑢W(xué)這樣寫:
request() .then(res => { // 假設(shè)請求成功碼 0 為成功 if (res.code === 0) { // ... } else if (res.code === 1) { // ... // 參數(shù)錯誤 } else { // ... // 其他錯誤 } })
同樣,非常不建議這樣使用,為什么呢。因為第一點,業(yè)務(wù)錯誤也是錯誤。then 里面處理的是請求成功結(jié)果,按照剛剛說的,如果處理不了錯誤就交由具體使用方去處理,而在這里業(yè)務(wù)錯誤應(yīng)該交由具體請求方的 catch 去處理。所以應(yīng)該改成:
request() .then(res => { // then 里面只會請求成功,并且能夠成功拿到數(shù)據(jù) // ... }) .catch(err => { // 獲取到包裝好的錯誤信息 if (err.code === 1) { // ... // 參數(shù)錯誤 } else { // ... // 其他錯誤 } })
這樣就能確保整個錯誤的傳遞路徑的正確性,而不是不該你處理的錯誤你處理了。 具體在 封裝 axios 的時候也有提到,就不展開了。
總結(jié)
我們先介紹了面向錯誤編程的概念,然后講了 js 在處理錯誤時的一些方法,以及最后提到了錯誤處理該由誰去做的問題。ok,結(jié)束。
到此這篇關(guān)于詳解JS中異常與錯誤處理的正確方法的文章就介紹到這了,更多相關(guān)JS異常處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript forEach函數(shù)實現(xiàn)代碼
在Base2中找到一個叫forEach的函數(shù),是我見過的最好的實現(xiàn)。挖出來分析一下。它能對各種普通對象,字符串,數(shù)組以及類數(shù)組進(jìn)行遍歷。如果原游覽器的對象已實現(xiàn)此函數(shù),它則調(diào)用原對象的函數(shù)。2010-01-01Javascript this 的一些學(xué)習(xí)總結(jié)
相信有C++、C#或Java等編程經(jīng)驗的各位,對于this關(guān)鍵字再熟悉不過了。由于Javascript是一種面向?qū)ο蟮木幊陶Z言,它和C++、C#或Java一樣都包含this關(guān)鍵字,接下來我們將向大家介紹Javascript中的this關(guān)鍵字2012-08-08全面了解函數(shù)聲明與函數(shù)表達(dá)式、變量提升
下面小編就為大家?guī)硪黄媪私夂瘮?shù)聲明與函數(shù)表達(dá)式、變量提升。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-08-08小程序websocket心跳庫(websocket-heartbeat-miniprogram)
這篇文章主要介紹了小程序websocket心跳庫(websocket-heartbeat-miniprogram),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02JavaScript中關(guān)于class的調(diào)用方法
下面小編就為大家?guī)硪黄琂avaScript中關(guān)于class的調(diào)用方法。具有很好的參考價值,希望對大家有所幫助2017-11-11layui select動態(tài)添加option的實例
下面小編就為大家分享一篇layui select動態(tài)添加option的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-03-03JavaScript 異步調(diào)用框架 (Part 6 - 實例 & 模式)
我們用了5篇文章來討論如何編寫一個JavaScript異步調(diào)用框架(問題 & 場景、用例設(shè)計、代碼實現(xiàn)、鏈?zhǔn)秸{(diào)用、鏈?zhǔn)綄崿F(xiàn)),現(xiàn)在是時候讓我們看一下在各種常見開發(fā)情景中如何使用它了。2009-08-08