欢迎光临易鼎网
详情描述
JS 中 try-catch 异常处理机制详解(结合 async/await)

1. 基本 try-catch 语法

1.1 基础用法

try {
  // 可能抛出异常的代码
  const result = riskyOperation();
  console.log('成功:', result);
} catch (error) {
  // 异常处理
  console.error('捕获到错误:', error.message);
} finally {
  // 无论是否发生异常都会执行
  console.log('清理工作完成');
}

1.2 Error 对象详解

try {
  throw new Error('自定义错误信息');
} catch (error) {
  console.log(error.name);      // "Error"
  console.log(error.message);   // "自定义错误信息"
  console.log(error.stack);     // 堆栈跟踪信息

  // 自定义错误类型
  if (error instanceof TypeError) {
    console.log('类型错误');
  } else if (error instanceof RangeError) {
    console.log('范围错误');
  } else if (error instanceof SyntaxError) {
    console.log('语法错误');
  }
}

2. 同步代码中的异常处理

2.1 同步异常示例

function divide(a, b) {
  if (b === 0) {
    throw new Error('除数不能为零');
  }
  return a / b;
}

function processData() {
  try {
    const result = divide(10, 0);
    console.log('结果:', result);
  } catch (error) {
    console.log('计算失败:', error.message);
    // 可以选择重新抛出
    // throw error;
  }
}

processData();

3. 异步代码中的异常处理

3.1 Promise 的异常处理

// 方式1:使用 catch 方法
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('请求失败:', error))
  .finally(() => console.log('请求结束'));

// 方式2:在 then 中处理错误
fetch('https://api.example.com/data')
  .then(
    response => response.json(),
    error => console.error('请求失败:', error)
  );

3.2 async/await 中的异常处理

3.2.1 基本用法
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    if (!response.ok) {
      throw new Error(`HTTP错误: ${response.status}`);
    }
    const data = await response.json();
    console.log('数据:', data);
    return data;
  } catch (error) {
    console.error('获取数据失败:', error.message);
    // 返回默认值或重新抛出
    return { defaultValue: true };
  }
}
3.2.2 多个异步操作
async function processMultipleRequests() {
  try {
    // 并行请求
    const [user, posts] = await Promise.all([
      fetch('/api/user').then(r => r.json()),
      fetch('/api/posts').then(r => r.json())
    ]);

    console.log('用户:', user);
    console.log('帖子:', posts);
  } catch (error) {
    console.error('请求失败:', error);
  }
}

4. 高级用法和最佳实践

4.1 封装错误处理函数

// 错误处理装饰器
function withErrorHandling(fn) {
  return async function(...args) {
    try {
      return await fn(...args);
    } catch (error) {
      console.error(`函数 ${fn.name} 执行失败:`, error);

      // 根据错误类型进行不同处理
      if (error instanceof NetworkError) {
        // 网络错误处理
        return retryOperation(fn, args);
      } else if (error instanceof ValidationError) {
        // 验证错误处理
        throw new UserFriendlyError('输入数据有误');
      }

      throw error; // 重新抛出未知错误
    }
  };
}

// 使用装饰器
const safeFetchData = withErrorHandling(fetchData);

4.2 自定义错误类

// 自定义错误类型
class AppError extends Error {
  constructor(message, code = 'UNKNOWN_ERROR') {
    super(message);
    this.name = this.constructor.name;
    this.code = code;
    this.timestamp = new Date().toISOString();
  }
}

class NetworkError extends AppError {
  constructor(message) {
    super(message, 'NETWORK_ERROR');
  }
}

class ValidationError extends AppError {
  constructor(message, field) {
    super(message, 'VALIDATION_ERROR');
    this.field = field;
  }
}

// 使用自定义错误
async function validateUser(input) {
  if (!input.email) {
    throw new ValidationError('邮箱不能为空', 'email');
  }

  try {
    await saveToDatabase(input);
  } catch (error) {
    throw new NetworkError('保存数据失败: ' + error.message);
  }
}

4.3 全局错误处理

// 全局 Promise 错误处理
window.addEventListener('unhandledrejection', event => {
  console.error('未处理的 Promise 拒绝:', event.reason);
  event.preventDefault(); // 防止默认错误处理
});

// 全局错误处理
window.addEventListener('error', event => {
  console.error('全局错误:', event.error);
  // 可以发送到错误监控服务
  sendToErrorMonitoring(event.error);
});

5. 实际应用场景

5.1 API 请求封装

class ApiClient {
  constructor(baseURL) {
    this.baseURL = baseURL;
  }

  async request(endpoint, options = {}) {
    try {
      const response = await fetch(`${this.baseURL}${endpoint}`, {
        headers: { 'Content-Type': 'application/json' },
        ...options
      });

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new ApiError(
          errorData.message || `HTTP ${response.status}`,
          response.status,
          errorData
        );
      }

      return await response.json();
    } catch (error) {
      if (error instanceof ApiError) {
        throw error;
      }

      // 网络错误
      if (error.name === 'TypeError' && error.message.includes('fetch')) {
        throw new NetworkError('网络连接失败');
      }

      throw error;
    }
  }
}

// 使用示例
const api = new ApiClient('https://api.example.com');

async function getUsers() {
  try {
    const users = await api.request('/users');
    return users;
  } catch (error) {
    if (error instanceof NetworkError) {
      // 显示网络错误提示
      showNotification('网络连接失败,请检查网络');
    } else if (error instanceof ApiError) {
      // 显示API错误信息
      showNotification(error.message);
    }
    return [];
  }
}

5.2 表单验证与提交

async function submitForm(formData) {
  try {
    // 验证数据
    validateFormData(formData);

    // 提交数据
    const response = await fetch('/api/submit', {
      method: 'POST',
      body: JSON.stringify(formData)
    });

    if (!response.ok) {
      throw new Error('提交失败');
    }

    const result = await response.json();
    showSuccessMessage('提交成功');
    return result;

  } catch (error) {
    if (error instanceof ValidationError) {
      // 显示字段级错误
      highlightErrorField(error.field, error.message);
    } else {
      // 显示通用错误
      showErrorMessage(error.message || '发生未知错误');
    }

    // 记录错误日志(非敏感信息)
    logError(error, { formId: 'user-registration' });

    // 重新抛出错误供上层处理
    throw error;
  } finally {
    // 清理工作,如禁用加载状态
    setLoadingState(false);
  }
}

6. 最佳实践总结

明确错误类型:使用自定义错误类,使错误处理更有针对性 不要静默忽略错误:即使只是记录,也要处理所有可能的错误 适当重新抛出:在 catch 块中处理后,如果无法完全恢复,应重新抛出 使用 finally:清理资源,无论是否发生错误 异步错误传播:注意 async 函数总是返回 Promise,错误可能传播到调用者 错误边界:在 React 等框架中,使用错误边界处理组件树中的错误 用户体验:向用户展示友好的错误信息,而不是技术细节 监控和日志:将重要错误发送到监控系统
// 综合示例
async function robustAsyncOperation() {
  let resource = null;

  try {
    resource = await acquireResource();
    const data = await processWithResource(resource);
    return await validateAndTransform(data);
  } catch (error) {
    if (error instanceof ValidationError) {
      console.warn('数据验证失败,使用默认值');
      return getDefaultData();
    }

    if (error instanceof TemporaryError) {
      console.log('临时错误,重试中...');
      return await retryOperation();
    }

    console.error('严重错误:', error);
    throw new CriticalError('操作失败', { cause: error });
  } finally {
    if (resource) {
      await releaseResource(resource);
    }
  }
}

通过合理使用 try-catch 和 async/await,可以编写出健壮、可维护的异步 JavaScript 代码。关键是根据具体场景选择合适的错误处理策略,既不过度捕获也不遗漏重要错误。