JavaScript 异常捕获完全指南(上):从同步异步到 Promise 错误处理

摘要:

本文系统解析 JavaScript 异常捕获的核心机制,涵盖同步代码、异步回调、Promise 链的完整错误处理方案。通过 20+ 实战场景,揭示未捕获异常的致命隐患,并对比不同处理模式的性能差异,为构建健壮应用奠定基础。


一、异常处理基础:同步代码捕获

try-catch-finally 标准结构

javascript 复制代码
function parseJSON(json) {  
  try {  
    console.log("开始解析");  
    const result = JSON.parse(json);  
    return result;  
  } catch (err) {  
    console.error("解析失败:", err.message);  
    return null;  
  } finally {  
    console.log("解析流程结束"); // 始终执行  
  }  
}  

parseJSON('{"name": "Alice"}'); // 成功  
parseJSON('invalid_json');      // 捕获错误  

性能关键点

javascript 复制代码
// 避免在 try 块中声明函数(V8 优化失效)  
try {  
  function riskyOperation() { /* ... */ } // 反模式!  
} catch (err) {}  

错误类型识别

javascript 复制代码
try {  
  // 可能抛出多种错误  
  localStorage.setItem('key', data); // 可能 QuotaExceededError  
  fetch(url); // 可能 TypeError  
} catch (err) {  
  if (err instanceof DOMException) {  
    handleStorageError(err);  
  } else if (err instanceof TypeError) {  
    handleNetworkError(err);  
  } else {  
    throw err; // 重新抛出未知错误  
  }  
}  

二、异步回调的异常困境

回调函数的错误吞噬

javascript 复制代码
// 无法捕获的异常  
try {  
  setTimeout(() => {  
    throw new Error("异步炸弹");  
  }, 100);  
} catch (err) {  
  console.log("永远不会执行");  
}  

// 控制台输出:  
// Uncaught Error: 异步炸弹  

Node.js 错误优先约定

javascript 复制代码
fs.readFile('config.json', (err, data) => {  
  if (err) {  
    return handleError(err); // 必须显式处理  
  }  
  processConfig(data);  
});  

解决方案:Domain 模块(已弃用)

javascript 复制代码
// 历史方案(仅作了解)  
const domain = require('domain');  
const d = domain.create();  

d.on('error', err => {  
  console.error("捕获异步错误:", err);  
});  

d.run(() => {  
  setTimeout(() => { throw new Error("被捕获") }, 100);  
});  

三、Promise 的异常处理艺术

基础错误捕获

javascript 复制代码
fetchData()  
  .then(data => process(data))  
  .catch(err => {  
    console.error("请求失败:", err);  
    return fallbackData;  
  });  

高级模式:错误冒泡阻断

javascript 复制代码
const p = new Promise((resolve, reject) => {  
  resolve("初始值");  
});  

p.then(val => {  
  throw new Error("步骤1失败");  
  return val;  
}).then(val => {  
  console.log("永远不会执行");  
}).catch(err => {  
  console.log("捕获:", err.message); // "步骤1失败"  
});  

finally 的不可替代性

javascript 复制代码
let loading = true;  

fetchData()  
  .then(data => render(data))  
  .catch(err => showError(err))  
  .finally(() => {  
    loading = false; // 无论成功失败都执行  
  });  

四、Async/Await 的优雅处理

基础 try-catch 模式

javascript 复制代码
async function loadUser() {  
  try {  
    const user = await fetchUser();  
    const orders = await fetchOrders(user.id);  
    return { user, orders };  
  } catch (err) {  
    console.error("加载失败:", err);  
    return { user: null, orders: [] };  
  }  
}  

并行请求的错误处理

javascript 复制代码
async function loadDashboard() {  
  try {  
    const [user, products] = await Promise.all([  
      fetchUser().catch(err => ({ error: err })),  
      fetchProducts().catch(err => ({ error: err }))  
    ]);  

    if (user.error || products.error) {  
      throw new AggregateError([user.error, products.error]);  
    }  

    return { user, products };  
  } catch (err) {  
    if (err instanceof AggregateError) {  
      err.errors.forEach(e => logError(e));  
    }  
    return null;  
  }  
}  

拒绝捕获的陷阱

javascript 复制代码
// 危险:未处理的拒绝  
async function riskyOperation() {  
  const promise = fetchData();  
  // ...其他操作  
  await promise; // 若此前请求已失败,此处抛出未捕获异常  
}  

// 安全方案  
async function safeOperation() {  
  const promise = fetchData().catch(err => err); // 预捕获  
  // ...  
  const result = await promise;  
  if (result instanceof Error) {  
    handleError(result);  
  }  
}  

五、全局异常捕获机制

浏览器环境

javascript 复制代码
// 同步错误  
window.onerror = (msg, url, line, col, err) => {  
  console.error(`全局捕获: ${err.stack}`);  
  return true; // 阻止默认控制台输出  
};  

// 异步错误  
window.addEventListener('error', event => {  
  console.error("全局异步错误:", event.error);  
});  

// Promise 拒绝  
window.addEventListener('unhandledrejection', event => {  
  console.error("未处理的拒绝:", event.reason);  
  event.preventDefault(); // 阻止控制台报错  
});  

Node.js 环境

javascript 复制代码
process.on('uncaughtException', err => {  
  console.error("未捕获异常:", err);  
  // 必须退出进程!  
  process.exit(1);   
});  

process.on('unhandledRejection', (reason, promise) => {  
  console.error("未处理的拒绝:", reason);  
});  

框架中的最佳实践

javascript 复制代码
// Express 错误处理中间件  
app.use((err, req, res, next) => {  
  if (err instanceof ValidationError) {  
    return res.status(400).json({ error: err.message });  
  }  
  logErrorToService(err);  
  res.status(500).send('服务器错误');  
});  

// Koa 错误处理  
app.use(async (ctx, next) => {  
  try {  
    await next();  
  } catch (err) {  
    ctx.status = err.status || 500;  
    ctx.body = { error: err.message };  
    ctx.app.emit('error', err, ctx); // 触发全局事件  
  }  
});  

结语

本篇深入探讨了 JavaScript 异常处理的基础与异步场景解决方案,核心要点:

  1. 同步代码使用 try-catch 结构化处理
  2. 异步回调需遵循错误优先约定
  3. Promise 链式捕获与 async/await 结合
  4. 全局捕获作为最后防线

本文是构建健壮应用的基石,如果对你有帮助,请点赞收藏支持!关注作者获取更多工程化实践指南。

相关推荐
沙蒿同学11 小时前
我把架构约定编译成了会变红的测试:Wails v2 + Go + Vue3 桌面脚手架实战
前端·后端·github
cpolar技术支持11 小时前
本地 Playwright 测试报告怎么远程复盘?Trace Viewer 跑起来后,用 cpolar 分享失败现场
前端·自动化测试·测试工具·cpolar·playwright
wordbaby11 小时前
企业级后台管理系统路由设计与最佳实践指南
前端
胡志辉的博客11 小时前
【完全开源】IP 纯净度检测 可一键部署到自己的CF
前端·javascript·chrome·ip·chromium
Hilaku11 小时前
作为面试官,我最怕遇到什么样的候选人?
前端·javascript·程序员
TiDi12 小时前
Pinia优化重复请求
前端
web3d52012 小时前
01-用 Leafletjs 10 分钟搭一张水利一张图(Vue3 + Vite 实战)
前端·javascript
晚安日记wanna12 小时前
Vue2 的 defineProperty 差在哪四层追问筛掉九成候选人
前端·vue.js·面试
TiDi12 小时前
吸顶导航交互实现
前端
kisshyshy12 小时前
从Props透传到自定义Hook:系统梳理React跨层级通信与逻辑复用
前端·架构·代码规范