Kodokon kodokon.com

调试与健壮性:try/catch、进阶 console、自定义错误

用专门的类、放置得当的捕获边界,以及一个充分发挥潜力的 console,构建一套明确的错误策略。

9 分钟 · 3 题

在 Kodokon 中打开本课

健壮性不在于捕获每一个错误,而在于在正确的地方捕获它们。实战准则:尽可能贴近 bug 之处,快速而响亮地失败(校验、不变量),并且只在你真正能够作出反应的边界处捕获:显示一条消息、重试、记录日志。一个什么有用的事都不做的 try/catch 并不会让代码更安全;它只会让失败变得悄无声息。

JAVASCRIPT
function parseConfig(json) {
  try {
    return JSON.parse(json);
  } catch (error) {
    console.error("Unreadable config:", error.message);
    throw error; // rethrow: nothing can be fixed here
  } finally {
    console.log("Finished reading configuration");
  }
}
先记录日志,再重新抛出:由上层的边界来决定。

纯字符串构不成一套错误策略。扩展 Error 来创建携带结构化数据(HTTP 状态、无效字段)的类,这些数据可以用 instanceof 来区分。自 ES2022 起,cause 选项会把原始错误链接到高层错误上:你既能暴露一条清晰的业务消息,又不会丢失技术线索。

JAVASCRIPT
class ApiError extends Error {
  constructor(message, status, options) {
    super(message, options);
    this.name = "ApiError";
    this.status = status;
  }
}
try {
  const low = new Error("socket timeout");
  throw new ApiError("Profile unavailable", 503,
    { cause: low });
} catch (error) {
  if (error instanceof ApiError) {
    console.error(error.status, error.cause.message);
  }
}
一个可区分的业务错误,且保留了技术层面的原因。

console 提供的远不止 logconsole.table 会把对象集合排列成表格,group/groupEnd 会嵌套输出,time/timeEnd 会测量一段时长,console.assert 只在条件为假才记录,而 console.trace 会打印当前的调用栈。这些工具组合起来,远胜于一场匿名 log 调用的倾盆大雨。

JAVASCRIPT
const users = [
  { name: "Ada", role: "admin" },
  { name: "Linus", role: "dev" },
];
console.table(users);
console.group("Checks");
console.assert(users.length > 0, "Empty list");
console.time("sort");
users.sort((a, b) => a.name.localeCompare(b.name));
console.timeEnd("sort");
console.groupEnd();
结构化输出:表格、分组、断言和计时器。

知识检测

确认你已牢记本课的重点内容。

  1. finally 块在什么时候运行?
    • 仅当没有抛出错误时
    • 仅当 catch 被触发时
    • 在任何情况下,包括在 return 或 throw 之后
  2. 在构造错误时,cause 选项是用来做什么的?
    • 阻止错误继续传播
    • 附上原始错误并保留技术上下文
    • 自动设置 HTTP 状态码
    • 触发一个新的 catch
  3. console.assert(condition, message) 的行为是什么?
    • 它仅在条件为假时才记录消息
    • 它在条件为假时抛出一个异常
    • 它仅在条件为真时才记录消息