JavaScript错误处理8个技巧,Bug减少75%!

错误处理往往是最容易被忽视的环节,但恰恰是它决定了应用的健壮性和用户体验。分享8个实用的JavaScript错误处理技巧,帮助我们构建更可靠的应用程序。

1. 使用 try-catch 包装异步代码

许多开发者认为 try-catch 只能处理同步代码,实际上通过适当的方式,它也能优雅地处理异步操作。

// ❌ 错误示范
async function fetchUserData() {
    const response = await fetch('/api/users');
    const data = await response.json();
    return data;
}

// ✅ 正确示范
async function fetchUserData() {
    try {
        const response = await fetch('/api/users');
        const data = await response.json();
        return data;
    } catch (error) {
        // 区分不同类型的错误
        if (error instanceof TypeError) {
            console.error('网络请求失败:', error.message);
            // 可以选择重试或返回缓存数据
        } else {
            console.error('解析数据失败:', error.message);
        }
        // 返回默认值或者抛出自定义错误
        return [];
    }
}

2. 实现全局错误处理器

全局错误处理可以捕获未被局部 try-catch 捕获的错误,是构建可靠应用的最后一道防线。

// 处理普通JavaScript错误
window.onerror = function(message, source, lineno, colno, error) {
    console.error({
        message,
        source,
        lineno,
        colno,
        error: error?.stack
    });
    // 向错误监控服务发送报告
    sendErrorReport({
        type: 'js_error',
        details: {message, source, lineno, colno}
    });
    return true; // 防止错误继续向上传播
};

// 处理未捕获的Promise错误
window.addEventListener('unhandledrejection', function(event) {
    console.error('未处理的Promise错误:', event.reason);
    event.preventDefault(); // 阻止默认处理
});

3. 自定义错误类型

创建自定义错误类型可以更好地区分和处理不同类别的错误。

图片

4. 优雅的错误降级处理

当遇到错误时,应该提供合理的降级方案,而不是让应用直接崩溃。

图片

5. 错误边界处理

在React应用中,使用错误边界可以防止整个应用因局部错误而崩溃。虽然普通JavaScript没有类似机制,但我们可以实现类似的隔离效果。

图片

6. 优化异步错误处理链

使用 Promise 链时,正确处理错误传播非常重要。

图片

7. 日志分级处理

建立合理的日志分级系统,可以更好地追踪和定位问题。

图片

8. 错误恢复机制

实现自动恢复机制,让应用在遇到错误后能够自动修复。

class ServiceManager {
    constructor(services) {
        this.services = new Map(services);
        this.healthChecks = new Map();
        this.startMonitoring();
    }

    async startService(name) {
        try {
            const service = this.services.get(name);
            await service.start();
            this.monitorService(name);
        } catch (error) {
            console.error(`服务 ${name} 启动失败:`, error);
            this.attemptRecovery(name);
        }
    }

    monitorService(name) {
        const healthCheck = setInterval(async () => {
            try {
                const service = this.services.get(name);
                const isHealthy = await service.checkHealth();
                if (!isHealthy) {
                    throw new Error('服务健康检查失败');
                }
            } catch (error) {
                this.handleServiceFailure(name, error);
            }
        }, 30000);
        
        this.healthChecks.set(name, healthCheck);
    }

    async handleServiceFailure(name, error) {
        console.error(`服务 ${name} 异常:`, error);
        
        // 停止健康检查
        clearInterval(this.healthChecks.get(name));
        this.healthChecks.delete(name);
        
        // 尝试重启服务
        await this.attemptRecovery(name);
    }

    async attemptRecovery(name, retries = 3) {
        for (let i = 0; i < retries; i++) {
            try {
                console.log(`尝试恢复服务 ${name},第 ${i + 1} 次`);
                await this.startService(name);
                console.log(`服务 ${name} 恢复成功`);
                return true;
            } catch (error) {
                console.error(`恢复尝试 ${i + 1} 失败:`, error);
                await new Promise(resolve => setTimeout(resolve, 5000 * (i + 1)));
            }
        }
        console.error(`服务 ${name} 恢复失败,已达到最大重试次数`);
        return false;
    }
}

好的错误处理不仅仅是捕获错误,而更重要的是优雅地处理这些错误,保证应用的正常运行。此外,错误处理应该是一个持续改进的过程,随着应用的发展,我们需要不断完善错误处理机制,以应对新出现的问题和挑战。

THE END