一、当JavaScript穿上滑板鞋:异步世界的崩塌危机

在Chrome浏览器疯狂输出的现代Web开发中,异步代码就像失控的传送带。让我们想象一个在线支付场景:用户在点击支付按钮后,系统需要依次执行购物车校验、支付渠道选择、银行接口通信三个异步操作。突然银行接口抛出了超时错误,此时若缺乏错误处理机制,这个错误就像落入黑洞的光线,消失在运行时中。

// 技术栈:浏览器环境 + 原生Promise
function processPayment() {
  validateCart()
    .then(selectPaymentChannel)
    .then(connectBankAPI)
    .catch(error => { // 最后的安全网
      console.error('支付流程爆炸:', error);
      trackError(error); // 错误上报
    });
}

// 典型错误场景
function connectBankAPI() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error('银行接口连接超时'));
    }, 5000);
  });
}

当这段代码在浏览器默默失败时,产品经理的夺命连环call就会准时响起:"用户说付款没反应!我们的错误监控系统在吃白饭吗?"

二、异步错误的百慕大三角:经典吞噬场景

1. Promise连锁反应

未处理的Promise拒绝就像被踢来踢去的皮球。试看这个订单查询功能:

// 技术栈:Node.js v14+ + ES6
async function queryOrder(userId) {
  const db = await connectDatabase(); // 可能断连的数据库
  return db.query(`
    SELECT * FROM orders 
    WHERE user_id = ${userId}
  `); // 潜在SQL注入?!
}

app.get('/orders', async (req, res) => {
  try {
    const orders = await queryOrder(req.user.id);
    res.json(orders);
  } catch (e) {
    // 这里的catch无法捕获query中的Promise拒绝
    res.status(500).send('服务器抽风');
  }
});

当数据库连接断开时,这个错误会直接导致进程崩溃,因为内部的Promise拒绝没有被正确处理。

2. setTimeout暗箭

定时器中的错误就像隐身刺客:

function checkSession() {
  setTimeout(() => {
    throw new Error('Session已过期'); // 跳脱三界外的错误
  }, 300000);
}

// 没有任何try...catch能捕获这个错误

3. 事件监听黑洞

WebSocket连接的异常处理:

// 技术栈:浏览器WebSocket API
const socket = new WebSocket('wss://trade.example.com');

socket.onmessage = async (event) => {
  const data = JSON.parse(event.data); // 可能格式错误
  await updateTradingView(data); // 可能异步异常
};

// 未被捕获的异常会使整个socket中断

三、构建错误结界:多维防护体系

1. Promise维度的引力场

使用ESLint配置强制要求catch处理:

// .eslintrc配置
{
  "rules": {
    "promise/catch-or-return": "error"
  }
}

// 生产环境代码
fetchOrderDetail(orderId)
  .then(renderDetail)
  .catch(e => {
    showErrorToast('加载详情失败');
    reportError(e); // Sentry上报
  });

2. async/await的全防护护盾

结合Babel编译的async函数错误追踪:

async function fetchUserAssets() {
  try {
    const auth = await checkAuthStatus(); // 可能失败
    const data = await fetch('/api/assets', { 
      headers: { Authorization: auth.token }
    });
    return parseJSON(data); // JSON解析可能出错
  } catch (e) {
    captureException(e); // Sentry捕捉
    throw wrapError(e, '资产加载失败'); // 错误包装
  }
}

3. 全局错误结界

浏览器环境的全域监控策略:

// 错误管理中心
class ErrorTracker {
  constructor() {
    window.addEventListener('error', this.handleRuntimeError);
    window.addEventListener('unhandledrejection', this.handlePromiseRejection);
  }

  handleRuntimeError = (event) => {
    this.report({
      type: 'RUNTIME_ERROR',
      message: event.message,
      stack: event.error.stack,
      filename: event.filename,
    });
    return true; // 阻止默认控制台报错
  };

  handlePromiseRejection = (event) => {
    this.report({
      type: 'PROMISE_REJECTION',
      reason: event.reason,
    });
    event.preventDefault(); // 阻止控制台警告
  };
}

四、错误监控中心建设:从本地到云端

1. 打造前端哨兵系统

搭建简易错误监控服务:

// 基于Express的监控服务
app.post('/log-error', (req, res) => {
  const { errorInfo, userAgent, pageURL } = req.body;
  
  // 入库前清洗数据
  const cleanedError = sanitizeError(errorInfo);
  
  db.collection('client_errors').insertOne({
    ...cleanedError,
    timestamp: new Date(),
    userAgent,
    pageURL,
    environment: process.env.NODE_ENV,
  });

  res.sendStatus(200);
});

// 客户端上报封装
function reportToServer(error) {
  navigator.sendBeacon('/log-error', {
    errorInfo: serializeError(error),
    userAgent: navigator.userAgent,
    pageURL: location.href,
  });
}

2. 接入专业监控平台

以Sentry为例的现代化整合:

// Sentry初始化配置
Sentry.init({
  dsn: 'https://your-key@sentry.io/project',
  integrations: [
    new Sentry.Integrations.GlobalHandlers({
      onerror: true,
      onunhandledrejection: true,
    }),
    new Sentry.Integrations.Breadcrumbs({
      console: true,
      dom: true,
    }),
  ],
  beforeSend(event) {
    if (event.user) {
      delete event.user.email; // 敏感信息过滤
    }
    return event;
  },
});

// React组件错误边界
class ErrorBoundary extends React.Component {
  componentDidCatch(error, info) {
    Sentry.withScope(scope => {
      scope.setExtras(info);
      Sentry.captureException(error);
    });
  }
}

五、实战:电商系统错误处理全流程

// 订单支付完整流程(技术栈:Node.js + Express + Axios)
app.post('/checkout', async (req, res) => {
  try {
    const paymentData = await validatePayment(req.body);
    const paymentResult = await processPayment(paymentData);
    await sendPaymentEmail(paymentResult);
    
    res.json({ success: true });
  } catch (error) {
    // 错误分类处理
    if (error instanceof PaymentGatewayError) {
      Sentry.addBreadcrumb({
        message: '支付网关错误',
        data: error.metadata,
      });
      res.status(503).json({ error: '支付通道拥堵' });
    } else if (error instanceof DatabaseError) {
      triggerDBAlet(error); // 触发运维告警
      res.status(500).json({ error: '系统维护中' });
    } else {
      res.status(400).json({ error: '请求参数异常' });
    }
    
    // 统一上报
    Sentry.captureException(error, {
      tags: { endpoint: '/checkout' },
    });
  }
});

// Axios请求拦截器
axios.interceptors.response.use(null, error => {
  if (!error.config) return Promise.reject(error);
  
  Sentry.addBreadcrumb({
    type: 'http',
    data: {
      url: error.config.url,
      method: error.config.method,
      status: error.response?.status,
    },
  });
  
  return Promise.reject(error);
});

六、监控体系的五角评估模型

1. 覆盖完备性

确保监控范围涵盖:

  • 运行时错误
  • 异步异常
  • 资源加载失败
  • API请求错误
  • 自定义业务异常

2. 信息完整性

每条错误记录应包含:

  • 错误堆栈(SourceMap解析)
  • 用户上下文(ID、操作路径)
  • 环境信息(浏览器、OS、分辨率)
  • 网络状态(在线/离线、网速)
  • 业务数据(购物车金额、操作步骤)

七、安全守则:错误处理的十二道金牌

  1. 生产环境禁用console.error
  2. 敏感信息过滤(身份证、银行卡号)
  3. 错误日志访问权限控制
  4. SourceMap文件隔离存储
  5. 错误采样率控制(避免DDoS)
  6. 监控系统熔断机制
  7. 错误自动分类算法
  8. 重复错误合并策略
  9. 实时报警阈值设定
  10. 错误生命周期管理
  11. GDPR日志保留策略
  12. 定期演练监控系统失效场景