错误处理基础 error-handling-fundamentals

这个技能用于指导和评估异步操作及API调用中的错误处理,包括try-catch、网络错误处理、用户友好错误消息等,提升代码健壮性和用户体验。关键词:错误处理,异步编程,API错误,try-catch,网络请求,前端开发。

前端开发 0 次安装 0 次浏览 更新于 3/8/2026

name: 错误处理基础 description: 指导异步操作和API调用的错误处理。当新手询问"如果失败怎么办"、“处理错误”、“try catch”、“网络错误”,或使用fetch、promises或外部服务构建功能时使用。

错误处理基础回顾

“错误不是失败——它们是信息。像对待有价值的数据一样处理它们。”

何时应用

在审查以下内容时激活此技能:

  • try/catch块
  • Promise链(.then/.catch)
  • API错误响应
  • 表单验证
  • 网络请求处理
  • 面向用户的错误消息

审查清单

错误捕获

  • [ ] 没有空捕获:每个catch块是否都执行有意义的操作?
  • [ ] 正确的作用域:错误是否在适当的级别被捕获?
  • [ ] 添加上下文:错误是否包含有用的调试信息?
  • [ ] 使用finally:清理操作是否放在finally块中?

用户体验

  • [ ] 用户友好的消息:用户能理解错误吗?
  • [ ] 不暴露技术细节:堆栈跟踪是否对用户隐藏?
  • [ ] 可操作的反馈:错误是否告诉用户下一步该做什么?
  • [ ] 优雅降级:在错误时,应用是否仍能部分工作?

日志记录与调试

  • [ ] 错误已记录:错误是否记录用于调试?
  • [ ] 日志中的上下文:日志是否包含请求ID、用户ID等?
  • [ ] 严重性级别:关键错误是否与警告区分?

恢复

  • [ ] 重试逻辑:此操作失败时是否应该重试?
  • [ ] 回退值:错误时是否有合理的默认值?
  • [ ] 加载状态:在重试时是否通知用户?

常见错误(反模式)

1. 空捕获(沉默的杀手)

❌ try {
     await submitForm();
   } catch (error) {
     // TODO: 稍后处理(永远不会发生)
   }

✅ try {
     await submitForm();
   } catch (error) {
     console.error('表单提交失败:', error);
     setError('无法提交。请重试。');
   }

2. 捕获过早

❌ function getUser(id) {
     try {
       return database.query(id);
     } catch {
       return null; // 调用者不知道失败了
     }
   }

✅ function getUser(id) {
     return database.query(id); // 让调用者决定
   }

   // 在UI层
   try {
     const user = await getUser(id);
   } catch (error) {
     showToast('无法加载用户');
   }

3. 通用错误消息

❌ catch (error) {
     setError('发生错误');
   }

✅ catch (error) {
     if (error.code === 'NETWORK_ERROR') {
       setError('检查您的互联网连接');
     } else if (error.code === 'NOT_FOUND') {
       setError('用户未找到');
     } else {
       setError('出了点问题。请重试。');
     }
   }

4. 泄露堆栈跟踪

❌ res.status(500).json({
     error: error.message,
     stack: error.stack
   });

✅ logger.error('请求失败', { error, requestId });
   res.status(500).json({
     error: '出了点问题'
   });

5. 忘记Finally

❌ try {
     setLoading(true);
     await fetchData();
     setLoading(false);
   } catch (error) {
     handleError(error);
     // 加载状态永远为真!
   }

✅ try {
     setLoading(true);
     await fetchData();
   } catch (error) {
     handleError(error);
   } finally {
     setLoading(false); // 始终运行
   }

苏格拉底式问题

向新手询问这些问题,而不是直接给出答案:

  1. 空捕获:“如果这悄无声息地失败,会发生什么?”
  2. 用户消息:“如果你是用户,这条消息会帮助您吗?”
  3. 恢复:“我们应该自动重试这个吗?”
  4. 作用域:“这是捕获此错误的合适地方吗?”
  5. 日志记录:“您将如何在生产环境中调试这个?”

错误消息指南

应该

  • 具体:“电子邮件地址已被注册”
  • 有帮助:“请检查您的互联网连接”
  • 提供下一步:“重试"或"联系支持”
  • 匹配严重性:关键错误比警告需要更多关注

不应该

  • 显示技术细节:TypeError: Cannot read property 'map' of undefined
  • 模糊:“发生错误”
  • 责怪用户:“您输入了无效数据”
  • 使用行话:“HTTP 500 内部服务器错误”

错误处理模式

API/网络错误

async function fetchWithRetry(url: string, retries = 3): Promise<Response> {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    return response;
  } catch (error) {
    if (retries > 0) {
      await sleep(1000);
      return fetchWithRetry(url, retries - 1);
    }
    throw error;
  }
}

表单验证

function validateForm(data: FormData) {
  const errors: Record<string, string> = {};

  if (!data.email) {
    errors.email = '电子邮件是必需的';
  } else if (!isValidEmail(data.email)) {
    errors.email = '请输入有效的电子邮件';
  }

  return {
    isValid: Object.keys(errors).length === 0,
    errors
  };
}

React错误边界

class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    logError(error, info);
  }

  render() {
    if (this.state.hasError) {
      return <FallbackUI />;
    }
    return this.props.children;
  }
}

标准参考

参见详细模式:

  • /standards/global/error-handling.md

需要指出的红旗

红旗 询问的问题
空捕获块 “当这失败时会发生什么?”
catch (e) { return null } “调用者如何知道它失败了?”
无加载状态 “用户在等待时看到什么?”
显示技术错误 “用户会理解这条消息吗?”
无finally进行清理 “加载状态在错误时是否卡住了?”
console.log而不是错误 “您将如何在生产日志中找到这个?”