JavaScript異常處理整理和思考

try-catch

try-catch 捕獲異常的時候只可以捕獲同步運行時的錯誤和異常,不能捕獲語法錯誤和異步執行代碼的錯誤和異常。javascript

try{
  // 同步代碼出錯
} catch(err) {
  // 捕獲同步錯誤
}
複製代碼

同步執行正常捕獲

程序代碼html

try {
    throw new Error('拋出異常')
} catch (err) {
    console.log("捕獲到了異常"+err)
}
複製代碼

執行結果前端

捕獲到了異常Error: 拋出異常
複製代碼

語法錯誤捕獲不到

少寫一個單引號,構造語法錯誤java

try {
    console.log('1)
} catch (err) {
    console.log("捕獲到了異常"+err)
}
複製代碼

執行結果node

console.log('1)
                ^^
SyntaxError: Invalid or unexpected token
複製代碼

通常而言,編輯器會有語法檢查,這個只是 try-catch 沒法捕獲的例子,實際中不多出現 git

異步錯誤捕獲不到

使用 setTimeout 異步執行拋出錯誤github

try {
    setTimeout(()=>{
        throw new Error('異步錯誤')
    })
} catch (err) {
    console.log("捕獲到了異常"+err)
}
複製代碼

執行結果ajax

throw new Error('異步錯誤')
        ^

Error: 異步錯誤
    at Timeout.setTimeout [as _onTimeout] (D:\fcode\try-catch.js:7:15)
複製代碼

思考

JavaScript代碼執行的過程promise

  • 第一階段:JS代碼從入口處開始執行,此時會依照Run-to-completion 從頭至尾執行同步代碼,並將執行過程當中產生的異步任務添加到事件隊列。
  • 第二階段:同步代碼執行完畢以後,經過事件循環檢查事件隊列執行此階段產生的異步任務,每個異步任務執行期間又當作了一個新的同步任務的執行,如此循環。
  • try-catch 是同步執行代碼,在第一階段的時候執行完畢,異步錯誤或者異常是在第二階段執行的,同步代碼不可能執行它執行完之後的任務,因此捕獲不到。
  • 異步任務的執行是脫離當前主線程的,只是一主線程共享內存和堆棧。(應該是吧,尚未具體考證)

promise中異常的捕獲

promise 相比於 try-catch 更加容易捕獲異步錯誤和異常。下面是一些注意點 瀏覽器

注意點

  • 直接經過return new Error('錯誤')的方式返回異常不能被下一個catch捕獲到,這裏返回的promiseresolve狀態,會被傳遞下去被then捕獲,只有throw new Error()才能被下一個catch捕獲
var promise = new Promise((resolve, reject) => {
      resolve('1')
    })
    promise.then((data) => {
      return new Error('錯誤')
    }).catch((err) => {
      console.log('不會執行')
    }).then((data) => {
      console.log('打印以前返回的錯誤', data)
    })
    // 打印以前返回的錯誤 Error: 錯誤
    // at promise.then (4.html:16)
複製代碼
  • then(success, error) then能夠由兩個處理函數,第一個用來處理resolved狀態的promise返回值,第二個用來處理rejected狀態的promise返回值,這裏處理的是鏈式調用的返回的前一個promise
var promise = new Promise((resolve, reject) => {
    reject('錯誤')
  })
  promise.then((data)=>{
    console.log('不會執行,promise狀態是rejected')
  }, (err)=>{
    // 捕獲到錯誤
    console.log(err) // 錯誤
  }).catch((err) => {
    // 鏈式調用的前一個promise返回是一個resolve狀態
    console.log('不能捕獲到錯誤')
  })
複製代碼
  • then中沒有指定處理rejected狀態的函數時,錯誤狀態會一直傳遞下去直到被處理,要是沒有處理不會被window捕獲,直接控制檯報錯...
window.addEventListener('error', (e) => {
      console.log(e)
    })
    var promise = new Promise((resolve, reject) => {
      reject('錯誤')
    })
    promise.then((data)=>{
      console.log('未對錯誤處理')
    }).catch((err) => {
      // 執行
      console.log('上一個then中缺省處理錯誤的函數,錯誤會被封裝成新的promise向下傳遞', err)
    })
    promise.then((data)=>{
      console.log('不處理,也沒有後續,錯誤會被window捕捉到')
    })
複製代碼
  • .then 的第二個處理錯誤的函數捕獲不了第一個處理成功的函數拋出的錯誤,然後續的 .catch 能夠捕獲以前的錯誤
var promise = new Promise((resolve, rejected) => {
    resolve(1)
  })
  promise.then((data)=>{
    throw new Error('2')
  }, (err) => {
    console.log('不能捕獲,then中第一個成功回調返回的錯誤')
  }).catch((err)=>{
    console.log('能夠捕獲上一個then中成功或者錯誤處理函數中拋出的錯誤')
  })
複製代碼

解決方案

node中

process.on('uncaughtException', function (err) {
	// todo
});
複製代碼

上述代碼必定要加在最前面,當 Node 發現一個未捕獲的異常時,會觸發這個事件。可是在 uncaughtException 的回調事件中會丟失環境的上下文。

在瀏覽器中

當 JS 運行時錯誤發生時,window 會觸發一個 ErrorEvent 接口的 error 事件,並執行 window.onerror()

window.onerror = function(msg, file, line, col, error) {
  // msg:錯誤信息(字符串)。
// file:發生錯誤的腳本URL(字符串)
// lineno:發生錯誤的行號(數字)
// col:發生錯誤的列號(數字)
// error:Error對象(對象)
  // todo
}
複製代碼

當一項資源(如圖片或腳本)加載失敗,加載資源的元素會觸發一個 Event 接口的 error 事件,並執行該元素上的onerror() 處理函數。這些 error 事件不會向上冒泡到 window 這樣用 onerror() 沒法捕捉到,此時可使用 window.addEventListener 捕獲。

網絡請求異常不是註冊在DOM交互事件,不會向上冒泡,在事件的捕獲階段處理。

window.addEventListener('error', (error) => {
    console.log('捕獲到異常:', error);
}, true)
複製代碼

參考

除了 try-catch 和 promise 的場景還會有其餘的一些異常處理,在平時接觸和閱讀文章的時候瞭解到還有

  • 靜態資源(圖片等)加載異常處理
  • ajax異步請求異常處理

這些實際遇到的較少,尚未太多的實戰經驗暫作記錄,等有了經驗以後再作補充。
參考文章以下:

相關文章
相關標籤/搜索