1许布、語法
try語句包含了由一個(gè)或者多個(gè)語句組成的try塊,和至少一個(gè)catch塊或者一個(gè)finally塊的其中一個(gè),或者兩個(gè)兼有粮彤,下面是三種形式的try聲明:
try...catch
try...finally
try...catch...finally
try {
// 需要被執(zhí)行的語句
} catch (err) {
// 如果在try塊里有異常被拋出時(shí)執(zhí)行的語句
} finally {
// 在try語句塊之后執(zhí)行的語句塊。無論是否有異常拋出或捕獲這些語句都將執(zhí)行姜骡。
}
上述代碼中导坟,try塊中的語句首先被執(zhí)行。如果運(yùn)行中發(fā)生了錯(cuò)誤圈澈,控制就會(huì)轉(zhuǎn)移到位于catch塊中語句惫周,其中括號(hào)中的err參數(shù)被作為例外變量傳遞。否則康栈,catch塊的語句被跳過不執(zhí)行递递。無論是發(fā)生錯(cuò)誤還是catch塊中的語句執(zhí)行完畢,或者沒有發(fā)生任何錯(cuò)誤try塊中的語句執(zhí)行完畢啥么,最后將執(zhí)行finally塊中的語句登舞。
2、舉例
1悬荣、無錯(cuò)誤情況
try {
alert('開始執(zhí)行 try1'); // (1)
alert('開始執(zhí)行 try2'); // (2)
} catch (err) {
alert('catch 被忽略菠秒,因?yàn)闆]有 error,不會(huì)被執(zhí)行'); // (3)
}
上面代碼會(huì)彈出‘開始執(zhí)行 try1’和開始執(zhí)行 try2氯迂,但是不會(huì)彈出‘catch 被忽略践叠,因?yàn)闆]有 error’
2言缤、有錯(cuò)誤情況
try {
alert('開始執(zhí)行 try1'); // (1)
throw "此處拋出錯(cuò)誤"; //try塊里到此結(jié)束,不會(huì)在執(zhí)行下邊的try2
alert('try2酵熙,不會(huì)被執(zhí)行'); // (2)
} catch (err) {
alert('出現(xiàn)了 error轧简,此句被執(zhí)行'); // (3)
}
上面代碼會(huì)彈出‘開始執(zhí)行 try1’和‘出現(xiàn)了 error,此句被執(zhí)行’
3匾二、try...catch 僅對(duì)可以運(yùn)行的代碼 error 有效哮独。
要使得 try...catch 能工作,代碼必須是可執(zhí)行的察藐。換句話說皮璧,它必須是有效的 JavaScript 代碼。
如果代碼包含語法錯(cuò)誤分飞,那么 try..catch 將無法正常工作悴务,例如下邊代碼出現(xiàn)了一行逗號(hào):
try {
,譬猫,讯檐,,染服,别洪,//js引擎無法理解這段代碼,它是無效的
throw "此處不會(huì)拋出錯(cuò)誤"; //不會(huì)執(zhí)行
alert('try2柳刮,此句不會(huì)被執(zhí)行'); // (2)
} catch (err) {
alert('此句不會(huì)被執(zhí)行'); // (3)
}
4挖垛、try catch 嵌套
try {
try {
throw new Error("try1");
}
finally {
console.log("finally");
}
}
catch (err) {
console.error("outer", err.message);
}
會(huì)輸出
// finally
// outer try1
try {
try {
throw new Error("try1");
}
catch (err) {
console.error("inner", err.message);
}
finally {
console.log("finally");
}
}
catch (err) {
console.error("outer", err.message);
}
會(huì)輸出
// inner try1
// finally
try {
try {
throw new Error("try1");
}
catch (err) {
console.error("inner", err.message);
// return 如果我在此處寫個(gè)return會(huì)是什么結(jié)果? (下邊的報(bào)錯(cuò)信息肯定不會(huì)拋出了秉颗,當(dāng)然return必須是在函數(shù)里)
throw new Error("try2"); //試想一下痢毒,如何把此句代碼放在下邊的finally里會(huì)怎么輸出?(結(jié)果一樣)
}
finally {
console.log("finally");
}
}
catch (err) {
console.error("outer", err.message);
}
會(huì)輸出
// inner try1
// finally
// outer try2
try {
try {
throw new Error("try1");
}
catch (err) {
console.error("inner", err.message);
throw new Error("try2");
}
finally {
console.log("finally");
return //終止了錯(cuò)誤的拋出
}
}
catch (err) {
console.error("outer", err.message); //不會(huì)被執(zhí)行
}
會(huì)輸出
// inner try1
// finally
5蚕甥、try...catch 異步任務(wù)
try...catch 不會(huì)捕獲到異步錯(cuò)誤:
try {
setTimeout(()=>{
throw new Error('錯(cuò)誤信息')
}, 1000);
} catch (err) {
alert( "alert不會(huì)執(zhí)行" );
}
因?yàn)?try...catch 包裹了計(jì)劃要執(zhí)行的函數(shù)哪替,該函數(shù)有延遲,這時(shí)js引擎已經(jīng)離開了 try...catch 結(jié)構(gòu)菇怀,也就是上下文環(huán)境已經(jīng)改變夷家,所以無法捕獲異步任務(wù)里的錯(cuò)誤。為了捕獲到計(jì)劃的函數(shù)中的異常敏释,那么 try...catch 必須在這個(gè)函數(shù)內(nèi),如下
setTimeout(() => {
try {
throw new Error("錯(cuò)誤信息");
} catch (err) {
alert("alert會(huì)執(zhí)行");
}
}, 1000);
6摸袁、try...catch promise對(duì)象的異常捕獲
try...catch 也不會(huì)捕獲微任務(wù)钥顽,如下代碼是捕獲不到錯(cuò)誤的。
function test(){
Promise.resolve('1').then(()=>{
throw new Error('錯(cuò)誤信息')
})
}
try {
test()
} catch (err) {
alert("不工作");
}
try {
Promise.reject('錯(cuò)誤信息');
} catch(e) {
console.log(e.message);
}
//這2段代碼try靠汁,catch都不會(huì)捕獲到錯(cuò)誤信息蜂大,因?yàn)?promise 內(nèi)部的錯(cuò)誤不會(huì)冒泡出來闽铐,而是被 promise 吃掉了,只有通過 promise.catch 才可以捕獲奶浦。
那我們順帶看下promise.catch
Promise.reject('錯(cuò)誤信息').then(v=>{
return '1'
}).catch(v=>{
console.log(v) // 錯(cuò)誤信息
})
Promise.resolve('1').then(v=>{
throw '錯(cuò)誤信息'
}).catch(v=>{
console.log(v) // 錯(cuò)誤信息
})
你會(huì)發(fā)現(xiàn)promise.reject的錯(cuò)誤和promise.resolve里throw的錯(cuò)誤都被promise.catch方法捕獲兄墅。
7、try...catch async/await 的異常捕獲
// 例子1
async function test() {
throw new Error('錯(cuò)誤信息');
return 0;
}
test().then().catch(e => console.log(e.message)); // 錯(cuò)誤信息(可以捕獲到錯(cuò)誤信息)
// 例子2
function promiseFn(){
return new Promise((resolve, reject) => {
throw new Error("錯(cuò)誤信息");
});
}
async function test() {
try {
const res = await promiseFn();
} catch (e) {
console.log(e.message);//錯(cuò)誤信息(可以捕獲到錯(cuò)誤信息)
}
}
test();
// 例子3
function promiseFn(){
new Promise((resolve, reject) => {
throw new Error("錯(cuò)誤信息");
});
}
async function test() {
try {
const res = await promiseFn();
} catch (e) {
console.log(e.message);//不會(huì)執(zhí)行 (捕獲不到錯(cuò)誤信息)
}
}
test();
// 例子3和例子2的區(qū)別在于promiseFn函數(shù)里沒有了return了
// 例子4
function promiseFn(){
return Promise.reject('錯(cuò)誤信息')
}
async function test() {
try {
const res = await promiseFn();
} catch (e) {
console.log(e);//錯(cuò)誤信息(可以捕獲到錯(cuò)誤信息)
}
}
test();
8澳叉、全局監(jiān)聽拋出的錯(cuò)誤
以上所有異常隙咸,僅通過 try catch、then 捕獲同步成洗、異步錯(cuò)誤五督。這些是局部錯(cuò)誤捕獲手段,當(dāng)我們無法保證所有代碼都處理了異常時(shí)瓶殃,可以進(jìn)行全局異常監(jiān)控充包,一般有兩種方法:
window.addEventListener('error')
window.addEventListener('unhandledrejection')
error 可以監(jiān)聽所有同步、異步的運(yùn)行時(shí)錯(cuò)誤遥椿,但無法監(jiān)聽語法基矮、接口、資源加載錯(cuò)誤冠场。而 unhandledrejection 可以監(jiān)聽到 Promise 中拋出的家浇,未被 .catch 捕獲的錯(cuò)誤。
window.addEventListener('error', function (e) {
var error = e.error;
console.log(error);//錯(cuò)誤信息
})
document.querySelector('button').addEventListener('click', () => {
throw new Error('錯(cuò)誤信息')
})