JavaScript 代码不能被阻断的稳定性建设

作者:小括号时间:2026-10-26 06:11:11浏览:456

本文探讨 JavaScript 代码因异常导致后续执行被阻断的风险,重点分析 try-catch 的使用局限。指出 throw 语句会阻断代码且仅限在 try 块内使用,异步代码无法被同步 try-catch 捕获。详细对比了 require 与 import() 的错误处理差异,强调 import() 作为 Promise 需采用链式 catch 方式,并提醒注意错误上报机制的完整性。

背景

在 JavaScript 开发中,确保代码的稳定性至关重要,核心原则是防止单个错误导致整个脚本执行流程被阻断。

JavaScript 代码可能因各种未预见的错误而中断,进而导致后续所有代码无法执行,这直接威胁到线上环境的稳定性。

最常见的 JavaScript 被阻断的情况

console.log(111)
// 预期 a = {}
// 结果
a = undefined
a.a = 1
console.log(222) // js代码不能执行到这一行

以下代码明显会抛出错误,错误始于 a.a = 1 这一行,导致后续的 JavaScript 代码执行流被阻断,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. 没考虑到错误上报

  • 上述示例代码未包含错误上报逻辑。当错误发生时,外部监控系统(即使接入了 Sentry 等产品)无法捕获到该错误,因为错误已被 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

网络上存在误导性的教程,建议使用 throw 语句。必须明确:throw 会阻断代码执行,这一点需要反复强调,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 块之外(包括 catch 块)直接使用 throw。

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 方法或 .catch() 链式调用进行处理。

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

结论:

  • try...catch 无法捕获 import() 模块加载过程中的错误,但 require 同步加载的错误可以被正常捕获。
  • 对于 import(),应使用 Promise 的方法进行 catch 处理。

背景:

  • require 属于运行时加载,其机制类似于函数调用。
  • import() 属于动态导入,执行延迟加载,属于异步任务(微任务),且返回 Promise 对象。

以上即为关于 JavaScript 代码稳定性建设中防止代码阻断的详细内容,更多关于 JavaScript 稳定建设的资料请关注本站其它相关文章!

作者最新文章