You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何替代window.onunhandledrejection实现跨浏览器Promise错误捕获与日志上报?

处理Promise错误并构建客户端错误日志器的最佳实践

首先,咱们先解决你最关心的Promise错误捕获问题,再帮你搭建一个健壮的独立错误日志器:

一、Promise链内的错误处理最佳方式

对于你自己编写的Promise代码,最可靠的做法是在每一条Promise链的末尾添加.catch(),这样能精准捕获链中任何环节抛出的错误,包括.then()回调里的异常:

// 示例:带错误处理的Promise链
fetch('/api/data')
  .then(response => {
    if (!response.ok) throw new Error(`HTTP错误:${response.status}`);
    return response.json();
  })
  .then(data => {
    // 处理数据,这里的错误会被下面的catch捕获
    processData(data);
  })
  .catch(error => {
    // 统一处理链内所有错误
    logger.reportError('promise_chain_error', error);
  });

这种方式比依赖全局事件更可控,能针对不同业务场景做定制化处理,同时避免全局事件的兼容性问题。

二、全局兜底:兼容所有未捕获的Promise拒绝

如果要捕获那些遗漏的、第三方库抛出的未处理Promise错误,window.onunhandledrejection是标准方案,但你当前的代码存在兼容性问题——e.detail.reason是旧版Chrome的非标准实现,标准属性是e.reason。我们需要做兼容处理:

window.addEventListener('unhandledrejection', (event) => {
  // 阻止浏览器默认的错误提示(可选)
  event.preventDefault();
  // 兼容标准和非标准实现
  const error = event.reason || (event.detail && event.detail.reason);
  if (error) {
    logger.reportError('unhandled_promise_rejection', error);
  }
});

三、构建独立的logger.js自动捕获所有错误

除了Promise错误,客户端还需要捕获同步脚本错误,我们把所有逻辑封装到独立文件中,实现全量错误自动上报:

// logger.js
class ErrorLogger {
  constructor(options = {}) {
    this.reportUrl = options.reportUrl || '/api/error-report';
    // 初始化全局错误监听
    this.initGlobalListeners();
  }

  // 封装错误上报逻辑
  async reportError(type, error) {
    try {
      // 收集错误详情:类型、消息、堆栈、当前URL、用户代理等
      const errorDetails = {
        type,
        message: error.message || '未知错误',
        stack: error.stack || '无堆栈信息',
        url: window.location.href,
        userAgent: navigator.userAgent,
        timestamp: new Date().toISOString()
      };

      // 上报到服务器(这里用fetch,也可以用XMLHttpRequest兼容旧浏览器)
      await fetch(this.reportUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(errorDetails)
      });
    } catch (reportError) {
      // 上报失败的话,可以存到localStorage,下次再尝试
      console.warn('错误上报失败:', reportError);
      this.saveToLocalStorage(type, error);
    }
  }

  // 初始化全局错误监听
  initGlobalListeners() {
    // 捕获同步脚本错误
    window.addEventListener('error', (event) => {
      event.preventDefault();
      this.reportError('sync_script_error', event.error);
    });

    // 捕获未处理的Promise拒绝
    window.addEventListener('unhandledrejection', (event) => {
      event.preventDefault();
      const error = event.reason || (event.detail && event.detail.reason);
      if (error) {
        this.reportError('unhandled_promise_rejection', error);
      }
    });
  }

  // 上报失败时缓存到localStorage
  saveToLocalStorage(type, error) {
    const cachedErrors = JSON.parse(localStorage.getItem('cached_errors')) || [];
    cachedErrors.push({
      type,
      message: error.message,
      stack: error.stack,
      timestamp: new Date().toISOString()
    });
    localStorage.setItem('cached_errors', JSON.stringify(cachedErrors));
  }

  // 可选:手动触发上报缓存的错误
  async reportCachedErrors() {
    const cachedErrors = JSON.parse(localStorage.getItem('cached_errors')) || [];
    if (cachedErrors.length === 0) return;

    try {
      await fetch(this.reportUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ batch: cachedErrors })
      });
      localStorage.removeItem('cached_errors');
    } catch (err) {
      console.warn('缓存错误上报失败:', err);
    }
  }
}

// 导出单例,方便全局使用
const logger = new ErrorLogger({ reportUrl: '/your-server-error-endpoint' });
// 页面加载完成后尝试上报缓存的错误
window.addEventListener('load', () => {
  logger.reportCachedErrors();
});

export default logger;

四、使用方式

在你的项目入口文件(比如main.js)中引入:

import logger from './logger.js';

// 之后所有未捕获的同步错误、Promise拒绝都会自动上报
// 也可以手动上报自定义错误
try {
  // 某些可能出错的逻辑
} catch (err) {
  logger.reportError('custom_error', err);
}

关键注意点

  • 避免过度依赖全局事件:全局事件是最后一道防线,尽量在Promise链内用.catch()处理错误,这样能更精准定位问题。
  • 兼容性:unhandledrejection事件在IE11及以下不支持,如果需要兼容,可以考虑用Promise polyfill(比如es6-promise)并结合自定义的错误捕获逻辑。
  • 堆栈信息优化:生产环境下代码可能被压缩,堆栈信息会变得模糊,建议在打包时保留sourcemap,方便服务器端解析真实代码位置。

内容的提问来源于stack exchange,提问作者Maximus Dredoff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:37:34