js中的錯誤處理(try...catch...finally,異步錯誤)

錯誤處理

在執行JavaScript代碼的時候,有些狀況下會發生錯誤,錯誤分兩種:
1.一種是程序寫的邏輯不對,致使代碼執行異常。例如:
var s = null;
var len = s.length; // TypeError:null變量沒有length屬性
對於這種錯誤,要修復程序。
2.另一種是執行過程當中,程序可能遇到沒法預測的異常狀況而報錯,例如,網絡鏈接中斷,讀取不存在的文件,沒有操做權限等。
對於這種錯誤,咱們須要處理它,並可能須要給用戶反饋。前端

錯誤處理是程序設計時必需要考慮的問題。對於C這樣貼近系統底層的語言,錯誤是經過錯誤碼返回的:bash

int fd = open("/path/to/file", O_RDONLY);複製代碼
if (fd == -1) {複製代碼
printf("Error when open file!");複製代碼
} else {複製代碼
// TODO複製代碼
}複製代碼

經過錯誤碼返回錯誤,就須要約定什麼是正確的返回值,什麼是錯誤的返回值。上面的 open()函數約定返回 -1表示錯誤。
顯然,這種用錯誤碼錶示錯誤在編寫程序時十分不便。
所以,高級語言一般都提供了更抽象的錯誤處理邏輯try ... catch ... finally,JavaScript也不例外。網絡

try ... catch ... finally

使用try ... catch ... finally處理錯誤時,咱們編寫的代碼以下:異步

let r1,r2,s = null;複製代碼
try{複製代碼
r1 = s.length;    // 此處會報錯複製代碼
r2 = 100;複製代碼
} catch {複製代碼
console.log('出錯' + e)複製代碼
} finally {複製代碼
cosole.log('finally')複製代碼
}複製代碼
console.log('r1 = ' + r1 )      // r1 is undefined複製代碼
console.log('r2 = ' + r2 )      // r2 is undefined複製代碼

運行後能夠發現,輸出提示相似「出錯了:TypeError: Cannot read property 'length' of null」。
咱們來分析一下使用try ... catch ... finally的執行流程。
當代碼塊被 try { ... }包裹的時候,就表示這部分代碼執行過程當中可能會發生錯誤,一旦發生錯誤,就再也不繼續執行後續代碼,轉而跳到 catch塊。
catch (e) { ... }包裹的代碼就是錯誤處理代碼,變量 e表示捕獲到的錯誤。

最後,不管有沒有錯誤,finally必定會被執行函數

執行流程

因此,有錯誤發生時,執行流程像這樣:
先執行 try { ... }的代碼;
執行到出錯的語句時,後續語句再也不繼續執行,轉而執行 catch (e) { ... }代碼;
最後執行 finally { ... }代碼。
而沒有錯誤發生時,執行流程像這樣:
先執行 try { ... }的代碼;
由於沒有出錯,catch (e) { ... }代碼不會被執行;
最後執行 finally { ... }代碼。
最後請注意,catch和 finally能夠沒必要都出現。
也就是說,try語句一共有三種形式:ui

完整的try ... catch ... finally:複製代碼
try {複製代碼
...複製代碼
} catch (e) {複製代碼
...複製代碼
} finally {複製代碼
...複製代碼
}複製代碼
只有try ... catch,沒有finally:複製代碼
try {複製代碼
...複製代碼
} catch (e) {複製代碼
...複製代碼
}複製代碼
只有try ... finally,沒有catch:複製代碼
try {複製代碼
...複製代碼
} finally {複製代碼
...複製代碼
}複製代碼

錯誤類型

JavaScript有一個標準的 Error對象表示錯誤,還有從 Error派生的 TypeError、ReferenceError等錯誤對象。咱們在處理錯誤時,能夠經過 catch(e)捕獲的變量 e訪問錯誤對象:spa

try {複製代碼
...複製代碼
} catch (e) {複製代碼
if (e instanceof TypeError) {複製代碼
alert('Type error!');複製代碼
} else if (e instanceof Error) {複製代碼
alert(e.message);複製代碼
} else {複製代碼
alert('Error: ' + e);複製代碼
}複製代碼
}複製代碼

使用變量 e是一個習慣用法,也能夠以其餘變量名命名,如 catch(ex)。線程

拋出錯誤

程序也能夠主動拋出一個錯誤,讓執行流程直接跳轉到 catch塊。拋出錯誤使用 throw語句。設計

throw new Error('這是一個錯誤')複製代碼

例如,前端讓用戶輸入一個數字,程序接收到的其實是一個字符串,而後用 parseInt()轉換爲整數。當用戶輸入不合法的時候,咱們就拋出錯誤;3d

實際上,JavaScript容許拋出任意對象,包括數字、字符串。可是,最好仍是拋出一個Error對象。
最後,當咱們用catch捕獲錯誤時,必定要編寫錯誤處理語句:

var n = 0, s;複製代碼
try {複製代碼
n = s.length;複製代碼
} catch (e) {複製代碼
console.log(e);複製代碼
}複製代碼
console.log(n);複製代碼
哪怕僅僅把錯誤打印出來,也不要什麼也不幹:複製代碼
var n = 0, s;複製代碼
try {複製代碼
n = s.length;複製代碼
} catch (e) {複製代碼
}複製代碼
console.log(n);複製代碼

由於catch到錯誤卻什麼都不執行,就不知道程序執行過程當中到底有沒有發生錯誤。
處理錯誤時,請不要簡單粗暴地用 alert()把錯誤顯示給用戶。教程的代碼使用 alert()是爲了便於演示

錯誤傳播

來源於:公衆號———前端大牛愛好者

若是代碼發生了錯誤,又沒有被try ... catch捕獲,那麼,程序執行流程會跳轉到哪呢?

function getLength(s) {複製代碼
return s.length;複製代碼
}複製代碼
複製代碼
function printLength() {複製代碼
console.log(getLength('abc')); // 3複製代碼
console.log(getLength(null)); // Error!複製代碼
}複製代碼
複製代碼
printLength();複製代碼

錯誤也具備冒泡傳播性
若是在一個函數內部發生了錯誤,它自身沒有捕獲,錯誤就會被拋到外層調用函數,若是外層函數也沒有捕獲,該錯誤會一直沿着函數調用鏈向上拋出,直到被JavaScript引擎捕獲,代碼終止執行。因此,咱們沒必要在每個函數內部捕獲錯誤,只須要在合適的地方來個統一捕獲,一網打盡:

當bar()函數傳入參數null時,代碼會報錯,錯誤會向上拋給調用方foo()函數,foo()函數沒有try ... catch語句,因此錯誤繼續向上拋給調用方 main()函數,main()函數有try ... catch語句,因此錯誤最終在main()函數被處理了。
至於在哪些地方捕獲錯誤比較合適,須要視狀況而定。

異步錯誤處理

來源於 廖雪峯老師博客

編寫JavaScript代碼時,咱們要時刻牢記,JavaScript引擎是一個事件驅動的執行引擎,代碼老是以單線程執行,而回調函數的執行須要等到下一個知足條件的事件出現後,纔會被執行。
例如,setTimeout()函數能夠傳入回調函數,並在指定若干毫秒後執行:

function printTime() {複製代碼
console.log('It is time!');複製代碼
}複製代碼
複製代碼
setTimeout(printTime, 1000);複製代碼
console.log('done');複製代碼

上面的代碼會先打印done,1秒後纔會打印It is time!。
若是printTime()函數內部發生了錯誤,咱們試圖用try包裹setTimeout()是無效的:

異步錯誤只能在異步的函數內部捕獲和處理

緣由就在於調用 setTimeout()函數時,傳入的 printTime函數並未馬上執行!緊接着,JavaScript引擎會繼續執行 console.log('done');語句,而此時並無錯誤發生。直到1秒鐘後,執行 printTime函數時才發生錯誤,但此時除了在 printTime函數內部捕獲錯誤外,外層代碼並沒有法捕獲。
因此,涉及到異步代碼,沒法在調用時捕獲,緣由就是在捕獲的當時,回調函數並未執行。
相似的,當咱們處理一個事件時,在綁定事件的代碼處,沒法捕獲事件處理函數的錯誤。
例如,針對如下的表單:

<form>複製代碼
<input id="x"> + <input id="y">複製代碼
<button id="calc" type="button">計算</button>複製代碼
</form>複製代碼
複製代碼


咱們用下面的代碼給button綁定click事件:

try{複製代碼
$btn.click(function(){複製代碼
var x = parseFloat($('#x').val()),複製代碼
var y = parseFloat($('#y').val()),複製代碼
r;複製代碼
if(isNaN(x) || isNaN(y)){複製代碼
throw new Error('輸入有誤');複製代碼
}複製代碼
r = x + y;複製代碼
alert('計算結果:' + r);複製代碼
})複製代碼
} catch(e) {複製代碼
alert('輸入有誤')複製代碼
}複製代碼

可是,用戶輸入錯誤時,處理函數並未捕獲到錯誤。請修復錯誤處理代碼。

參考文章:
(1) mp.weixin.qq.com/s?__biz=MzU…
(2) mp.weixin.qq.com/s?__biz=MzU…
(3)mp.weixin.qq.com/s/oJJNxgG95…

相關文章
相關標籤/搜索