首頁 > 軟體

JavaScript程式碼不能被阻斷的穩定性建設

2022-10-12 14:01:29

背景

穩定性建設之JavaScript程式碼不能被阻斷

js程式碼可能會因為某些原因,導致出錯,進而整個後續程式碼有可能都被阻斷。直接影響線上的穩定性

最常見的js被阻斷的情況

console.log(111)
// 預期 a = {}
// 結果
a = undefined
a.a = 1
console.log(222) // js程式碼不能執行到這一行

這個程式碼很明顯會報錯,在a.a = 1這一行開始報錯,後續的js程式碼被阻斷了,console.log(222)列印不出來

解決辦法

  • 解決辦法也很簡單,用 try...catch... 捕獲住錯誤就好了
console.log(111)
try {
  // 預期 a = {}
  // 結果
  a = undefined
  a.a = 1
} catch (e) {
  console.error(e)
}
console.log(222) // js程式碼可以執行到這一行

容易被我們忽視的點

1. 沒考慮到錯誤上報

  • 上面的demo沒有考慮錯誤上報,發生錯誤時,外部根本捕獲不到(即使你接入了sentry類的產品),因為error被try catch給吃掉了
try {
  // 預期 a = {} 
  // 結果 
  a = undefined 
  a.a = 1
} catch (e) {
  console.error(e)
  // 公司內部的上報函數
  someReportFunction('sendEvent', {
    name: 'try_catch_error',
    params: {
      errorMsg: e.message,
      errorStack: e.stack
    },
  });
}

2. 錯用throw

隨便點開一篇文章,就有人在誤人子弟,教別人用 throwthrow這個東西是不能亂用的,因為他會阻斷程式碼,重要的事情說三遍,throw會阻斷程式碼,throw會阻斷程式碼,throw會阻斷程式碼

例如:

console.log(111)
try {
  // 預期 a = {}
  // 結果
  a = undefined
  a.a = 1
} catch (e) {
  console.error(e)
  throw e // throw會阻斷程式碼,導致下面不執行
}
console.log(222) // 不能執行到這一行

當然throw也不是一無是處,但是,他只能在try{ 裡面使用 },不能在try之外的地方使用throw,包括catch

console.log(111)
try {
  throw new Error(111)
} catch (e) {
  console.error(e)
}
console.log(222)
function getData () {
    if (...) {
        ...
    } else {
        throw new Error(111)
    }
}
console.log(111)
try {
  getData()
} catch (e) {
  console.error(e)
}
console.log(222)

3. 非同步程式碼catch不到,還是會被阻斷

console.log(111111111)
try {
  setTimeout(() => {
    a = undefined
    a.a = 1 // 程式碼被阻斷於此
    console.log('error') // 不能執行到這一行
  }, 0)
} catch (e) {
  console.error(e) // 非同步程式碼catch不到
}
console.log(222222222)
setTimeout(() => {
  console.log('setTimeout') // 瀏覽器可以執行到這一行,node的不行(node14和16版本都test了)
}, 2000)

4. import()和require()的錯誤捕獲表現不一致

// a.js
console.log(111111111)
try {
  require('./b.js')
} catch (e) {
  console.log('error') // 錯誤會被正常catch到
  console.error(e)
}
console.log(222222222)
setTimeout(() => {
  console.log('setTimeout')
}, 2000)
// b.js
console.log(1)
a = undefined
a.a = 1
console.log(2)
// 結果列印 (require被正常捕獲)
111111111
1
error
TypeError: Cannot set property 'a' of undefined
    ...
    ...
222222222
setTimeout
  • 同樣的程式碼換成,import()
// a.js
console.log(111111111)
try {
  import('./b.js')
} catch (e) {
  console.log('error') // 錯誤沒有被catch到
  console.error(e) 
}
console.log(222222222)
setTimeout(() => {
  console.log('setTimeout')
}, 2000)
// b.js
console.log(1)
a = undefined
a.a = 1
console.log(2)
// 結果列印 (import的 錯誤沒有被catch到)
111111111
222222222
1
(node:92673) UnhandledPromiseRejectionWarning: TypeError: Cannot set property 'a' of undefined
    ...
setTimeout

正確捕獲import()的方式:其實import()是一個promise,用promise的方法去catch就好了

import('./b.js') 
  .catch(e => { 
    console.log('error') 
    console.error(e) 
  })

結論:

  • try catch 不能捕獲import()模組的錯誤,require可以被捕獲
  • import() 用promise的方法去catch就好了

背景:

  • require是執行時載入(可以理解為,函數呼叫)
  • import()是動態import,會延遲載入,是非同步任務(微任務),是promise

以上就是JavaScript程式碼不能被阻斷的穩定性建設的詳細內容,更多關於JavaScript穩定建設的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com