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. 全局捕获作为最后防线

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

相关推荐
RadiumAg41 分钟前
记一道有趣的面试题
前端·javascript
yangzhi_emo1 小时前
ES6笔记2
开发语言·前端·javascript
yanlele1 小时前
我用爬虫抓取了 25 年 5 月掘金热门面试文章
前端·javascript·面试
中微子2 小时前
React状态管理最佳实践
前端
烛阴2 小时前
void 0 的奥秘:解锁 JavaScript 中 undefined 的正确打开方式
前端·javascript
中微子2 小时前
JavaScript 事件与 React 合成事件完全指南:从入门到精通
前端
Hexene...3 小时前
【前端Vue】如何实现echarts图表根据父元素宽度自适应大小
前端·vue.js·echarts
初遇你时动了情3 小时前
腾讯地图 vue3 使用 封装 地图组件
javascript·vue.js·腾讯地图
dssxyz3 小时前
uniapp打包微信小程序主包过大问题_uniapp 微信小程序时主包太大和vendor.js过大
javascript·微信小程序·uni-app
天天扭码3 小时前
《很全面的前端面试题》——HTML篇
前端·面试·html