日常擼代碼的時候,try catch 用的太多了,特別是一些 」安全感" 低的人,基本是處處 try catch,生怕 JS 報錯,而後頁面整個掛掉了。前端
其實爲啥會安全感低呢,是由於界限模糊。web
因此,咱們要作一個 「安全感」 高的碼農[狗頭][狗頭][狗頭]面試
面試官:麻煩用一句話描述 JS 異常是否能被 try catch 到?promise
面試者:異步方法沒法捕捉到……安全
面試官:不要背答案,麻煩用一句話描述 JS 異常是否能被 try catch 到!微信
面試者:沉默 ing …………異步
面試者:能捕捉到的異常,必須是線程執行已經進入 try catch 但 try catch 未執行完的時候拋出來的。編輯器
面試官: 沉默 ing …………ui
面試官:啥時候能夠來上班?url
歡笑交談中,拿到 offer …………
咱們咱們來分析下這個一句話描述 try catch 的含義。
主要分爲三段:try catch 以前,之中,以後。
代碼報錯的時候,線程執行未進入 try catch,那麼沒法捕捉異常。
好比語法異常(syntaxError),由於語法異常是在語法檢查階段就報錯了,線程執行還沒有進入 try catch 代碼塊,天然就沒法捕獲到異常。
例子 1:
try{
a.
}catch(e){
console.log("error",e);
}
// output
Uncaught SyntaxError: Unexpected token '}'
複製代碼
代碼報錯的時候,線程執行處於 try catch 之中,則能捕捉到異常。
看以下例子:
try{
function d(){a.b;}
d();
}catch(e){
console.log("error",e);
}
// output
error ReferenceError: a is not defined
複製代碼
function d(){a.b;}
try{
d();
}catch(e){
console.log("error",e);
}
// output
error ReferenceError: a is not defined
複製代碼
上述報錯的時機,都是代碼執行進入了 try catch ,執行 d() 方法的時候,線程執行處在 try 裏面,因此能捕捉到。
代碼報錯的時候,線程已經執行完 try catch,這種不能捕捉到異常。
例子:
try{
setTimeout(()=>{
console.log(a.b);
}, 100)
}catch(e){
console.log('error',e);
}
console.log(111);
//output
111
Uncaught ReferenceError: a is not defined
複製代碼
setTimeout 裏面報錯,其實是 100ms 以後執行的代碼報錯,此時代碼塊 try catch 已經執行完成,111 都已經被執行了,故沒法捕捉異常。
例子:
try{
function d(){a.b;}
}catch(e){
console.log("error",e);
}
console.log(111);
d();
// output
111
Uncaught ReferenceError: a is not defined
複製代碼
方法定義在 try catch 代碼塊裏面,可是執行方法在 try catch 外,在執行 d 方法的時候報錯,此時 try catch 已經執行完成,111 都已經被執行了,故而沒法捕捉異常。
能被 try catch 捕捉到的異常,必須是在報錯的時候,線程執行已經進入 try catch 代碼塊,且處在 try catch 裏面,這個時候才能被捕捉到。
若是是在以前,或者以後,都沒法捕捉異常。
敲黑板:不要死記硬背,啥能夠捕獲,啥不能捕獲!記住這一句話,永遠不會忘!
相對於外部 try catch,Promise 沒有異常!
例子 6:
try{
new Promise(function (resolve, reject) {
a.b;
}).then(v=>{
console.log(v);
});
}catch(e){
console.log('error',e);
}
// output
Uncaught (in promise) ReferenceError: a is not defined
複製代碼
看如上報錯,線程在執行 a.b 的時候,事實上屬於同步執行,try catch 並未執行完成,按理應該能捕捉到異常,這裏爲啥沒法捕捉呢?
事實上,Promise 的異常都是由 reject 和 Promise.prototype.catch 來捕獲,無論是同步仍是異步。
核心緣由是由於 Promise 在執行回調中都用 try catch 包裹起來了,其中全部的異常都被內部捕獲到了,並未往上拋異常。
以下來自 Promises/A+ 的實現 then/promise 源碼:
function getThen(obj) {
try {
return obj.then;
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
function tryCallOne(fn, a) {
try {
return fn(a);
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
function tryCallTwo(fn, a, b) {
try {
fn(a, b);
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
複製代碼
能夠看到,這裏執行 then (Promise.prototype.then 回調), tryCallTwo (doResolve 回調), tryCallOne (handleResolved 回調) 方法都被 try catch了。
異常都被包裹起來了。因此異常都不會被外層的 try catch 捕捉,所以在外層的 try catch 看來,Promise 根本沒有異常,事實上也確實沒有「異常」,好比:
try{
new Promise(function (resolve, reject) {
a.b;
}).then(v=>{
console.log(v);
});
console.log(111);
}catch(e){
console.log('error',e);
}
console.log(222);
// output
111
222
Uncaught (in promise) ReferenceError: a is not defined
複製代碼
顯然,a.b 報錯以後的,111 和 222 都能正常運行,promise 的異常都已經被內部 catch 了,在外層的 try catch 看來就是沒有異常,線程繼續執行。
try catch 沒法捕捉 Promise 的異常,是由於 Promise 的異常沒有往上拋。
再看一例:
function a(){
return new Promise((resolve, reject) =>{
setTimeout(() => {
reject(1);
})
})
}
try{
await a();
}catch(e){
console.log('error',e);
}
console.log(111);
//output
error 1
111
複製代碼
這個例子的異常被 catch 捕獲到了,那麼這裏的 Promise 爲啥能捕獲到異常呢?
咱們仍是看開始的「一句話總結」
報錯的時候( setTimeout 裏面的 reject ),線程執行已經進入 try catch 代碼塊,可是並未執行完成,這樣的話固然能夠捕獲到異常。await 將代碼執行停留在 try catch 代碼塊裏面的緣故。
敲黑板了: 不要用 try catch 包裹 Promise , Promise 很強大,不用擔憂異常會往上拋!咱們只須要給 Promise 增長 Promise#catch 就 OK 了