热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

浅谈前端中的错误处理

某一天用户反馈打开的页面白屏幕,怎么定位到产生错误的原因呢?日常某次发布怎么确定发布会没有引入bug呢?此时捕获到代码运行的bug并上报是多么的重要。既然捕获错误并上报是日常开发中

某一天用户反馈打开的页面白屏幕,怎么定位到产生错误的原因呢?日常某次发布怎么确定发布会没有引入bug呢?此时捕获到代码运行的bug并上报是多么的重要。

既然捕获错误并上报是日常开发中不可缺少的一环,那怎么捕获到错误呢?万能的**try…catch**


try{
throw new Error()
} catch(e) {
// handle error
}

看上去错误捕获是多么的简单,然而下面的场景下就不能捕获到了


try {
setTimeout(() => {
throw new Error('error')
})
} catch (e) {
// handle error
}

你会发现上面的例子中的错误不能正常捕获,看来错误捕获并不是这样简单**try…catch**就能搞定,当然你也可以为异步函数包裹一层**try…catch**来处理。

浏览器中,**window.onerror**来捕获你的错误


window.Onerror= function (msg, url, row, col, error) {
console.log('error');
console.log({
msg, url, row, col, error
})
};

捕获到错误后就可以将错误上报,上报方式很简单,你可以通过创建简单的**img**,通过**src**指定上报的地址,当然为了避免上报发送过多的请求,可以对上报进行合并,合并上报。可以定时将数据进行上报到服务端。

但但你去看错误上报的信息的时候,你会发现一些这样的错误**Script error**

因为浏览器的同源策略,对于不同域名的错误,都抛出了**Script error**,怎么解决这个问题呢?特别是现在基本上js资源都会放在cdn上面。

解决方案

1:所有的资源都放在同一个域名下。但是这样也会存在问题是不能利用cdn的优势。

2:增加跨域资源支持,在cdn 上增加支持主域的跨域请求支持,在script 标签加**crossorigin**属性

在使用Promise过程中,如果你没有catch,那么可以这样来捕获错误

window.addEventListener("unhandledrejection", function(err, promise) {
// handle error here, for example log
});

如何在NodeJs中捕获错误

NodeJs中的错误捕获很重要,因为处理不当可能导致服务雪崩而不可用。当然了不仅仅知道如何捕获错误,更应该知道如何避免某些错误。

  • 当你写一个函数的时候,你也许曾经思考过当函数执行的时候出现错误的时候,我是应该直接抛出throw,还是使用callback或者event emitter还是其它方式分发错误呢?
  • 我是否应该检查参数是否是正确的类型,是不是null
  • 如果参数不符合的时候,你怎么办呢?抛出错误还是通过callback等方式分发错误呢?
  • 如果保存足够的错误来复原错误现场呢?
  • 如果去捕获一些异常错误呢?try…catch还是domain

操作错误VS编码错误

1. 操作错误

操作错误往往发生在运行时,并非由于代码bug导致,可能是由于你的系统内存用完了或者是由于文件句柄用完了,也可能是没有网络了等等

2.编码错误

编码错误那就比较容易理解了,可能是undefined却当作函数调用,或者返回了不正确的数据类型,或者内存泄露等等

处理操作错误

  • 你可以记录一下错误,然后什么都不做
  • 你也可以重试,比如因为链接数据库失败了,但是重试需要限制次数
  • 你也可以将错误告诉前端,稍后再试
  • 也许你也可以直接处理,比如某个路径不存在,则创建该路径

处理编码错误

错误编码是不好处理的,因为是由于编码错误导致的。好的办法其实重启该进程,因为

  • 你不确定某个编码错误导致的错误会不会影响其它请求,比如建立数据库链接错误由于编码错误导致不能成功,那么其它错误将导致其它的请求也不可用
  • 或许在错误抛出之前进行IO操作,导致IO句柄无法关闭,这将长期占有内存,可能导致最后内存耗尽整个服务不可用。
  • 上面提到的两点其实都没有解决问题根本,应该在上线前做好测试,并在上线后做好监控,一旦发生类似的错误,就应该监控报警,关注并解决问题

如何分发错误

  • 在同步函数中,直接throw出错误
  • 对于一些异步函数,可以将错误通过callback抛出
  • async/await可以直接使用try..catch捕获错误
  • EventEmitter抛出error事件

NodeJs的运维

一个NodeJs运用,仅仅从码层面是很难保证稳定运行的,还要从运维层面去保障。

多进程来管理你的应用

单进程的nodejs一旦挂了,整个服务也就不可用了,所以我萌需要多个进程来保障服务的可用,某个进程只负责处理其它进程的启动,关闭,重启。保障某个进程挂掉后能够立即重启。

可以参考TSW中多进程的设计。master负责对worker的管理,worker和master保持这心跳监测,一旦失去,就立即重启之。

domain

process.on('uncaughtException', function(err) {
console.error('Error caught in uncaughtException event:', err);
});
process.on('unhandleRejection', function(err) {
// TODO
})

上面捕获nodejs中异常的时候,可以说是很暴力。但是此时捕获到异常的时候,你已经失去了此时的上下文,这里的上下文可以说是某个请求。假如某个web服务发生了一些异常的时候,还是希望能够返回一些兜底的内容,提升用户使用体验。比如服务端渲染或者同构,即使失败了,也可以返回个静态的html,走降级方案,但是此时的上下文已经丢失了。没有办法了。

function domainMiddleware(options) {
return async function (ctx, next) {
const request = ctx.request;
const d = process.domain || domain.create();
d.request = request;
let errHandler = (err) => {
ctx.set('Content-Type', 'text/html; charset=UTF-8');
ctx.body = options.staticHtml;
};
d.on('error', errHandler);
d.add(ctx.request);
d.add(ctx.response);
try {
await next();
} catch(e) {
errHandler(e)
}
}

上面是一个简单的koa2的domain的中间件,利用domain监听error事件,每个请求的Request, Response对象在发生错误的时候,均会触发error 事件,当发生错误的时候,能够在有上下文的基础上,可以走降级方案。

如何避免内存泄露

内存泄漏很常见,特别是前端去写后端程序,闭包运用不当,循环引用等都会导致内存泄漏。

  • 不要阻塞Event Loop的执行,特别是大循环或者IO同步操作

    for ( var i = 0; i <10000000; i++ ) {
    var user = {};
    user.name = 'outmem';
    user.pass = '123456';
    user.email = 'outmem[@outmem](/user/outmem).com';
    }

    上面的很长的循环会导致内存泄漏,因为它是一个同步执行的代码,将在进程中执行,V8在循环结束的时候,是没办法回收循环产生的内存的,这会导致内存一直增长。还有可能原因是,这个很长的执行,阻塞了node进入下一个Event loop, 导致队列中堆积了太多等待处理已经准备好的回调,进一步加剧内存的占用。那怎么解决呢?

    可以利用setTimeout将操作放在下一个loop中执行,减少长循环,同步IO对进程的阻.阻塞下一个loop 的执行,也会导致应用的性能下降

  • 模块的私有变量和方法都会常驻在内存中

var leakArray = [];
exports.leak = function () {
leakArray.push("leak" + Math.random());
};

在node中require一个模块的时候,最后都是形成一个单例,也就是只要调用该函数一下,函数内存就会增长,闭包不会被回收,第二是leak方法是一个私有方法,这个方法也会一直存在内存。加入每个请求都会调用一下这个方法,那么内存一会就炸了。

这样的场景其实很常见

// main.js
function Main() {
this.greeting = 'hello world';
}
module.exports = Main;

var a = require('./main.js')();
var b = require('./main.js')();
a.greeting = 'hello a';
console.log(a.greeting); // hello a
console.log(b.greeting); // hello a

require得到是一个单例,在一个服务端中每一个请求执行的时候,操作的都是一个单例,这样每一次执行产生的变量或者属性都会一直挂在这个对象上,无法回收,占用大量内存。

其实上面可以按照下面的调用方式来调用,每次都产生一个实例,用完回收。

var a = new require('./main.js');
// TODO

有的时候很难避免一些可能产生内存泄漏的问题,可以利用vm每次调用都在一个沙箱环境下调用,用完回收调。

  • 最后就是避免循环引用了,这样也会导致无法回收

推荐阅读
  • 在 Linux 系统中,`/proc` 目录实现了一种特殊的文件系统,称为 proc 文件系统。与传统的文件系统不同,proc 文件系统主要用于提供内核和进程信息的动态视图,通过文件和目录的形式呈现。这些信息包括系统状态、进程细节以及各种内核参数,为系统管理员和开发者提供了强大的诊断和调试工具。此外,proc 文件系统还支持实时读取和修改某些内核参数,增强了系统的灵活性和可配置性。 ... [详细]
  • 在MFC开发过程中,利用Windows内置的文件对话框可以显著提高文件操作的效率。本文总结了使用文件对话框进行文件选择和处理的经验,详细介绍了相关API的调用方法和参数设置,如`CFileDialog`类的使用、结构体`OPENFILENAME`的配置以及如何获取选中的文件路径。通过这些技巧,开发者可以快速实现文件的打开、保存等功能,提升应用程序的用户体验。 ... [详细]
  • HBase在金融大数据迁移中的应用与挑战
    随着最后一台设备的下线,标志着超过10PB的HBase数据迁移项目顺利完成。目前,新的集群已在新机房稳定运行超过两个月,监控数据显示,新集群的查询响应时间显著降低,系统稳定性大幅提升。此外,数据消费的波动也变得更加平滑,整体性能得到了显著优化。 ... [详细]
  • Python学习:环境配置与安装指南
    Python作为一种跨平台的编程语言,适用于Windows、Linux和macOS等多种操作系统。为了确保本地已成功安装Python,用户可以通过终端或命令行界面输入`python`或`python3`命令进行验证。此外,建议使用虚拟环境管理工具如`venv`或`conda`,以便更好地隔离不同项目依赖,提高开发效率。 ... [详细]
  • 【前端开发】深入探讨 RequireJS 与性能优化策略
    随着前端技术的迅速发展,RequireJS虽然不再像以往那样吸引关注,但其在模块化加载方面的优势仍然值得深入探讨。本文将详细介绍RequireJS的基本概念及其作为模块加载工具的核心功能,并重点分析其性能优化策略,帮助开发者更好地理解和应用这一工具,提升前端项目的加载速度和整体性能。 ... [详细]
  • 进程(Process)是指计算机中程序对特定数据集的一次运行活动,是系统资源分配与调度的核心单元,构成了操作系统架构的基础。在早期以进程为中心的计算机体系结构中,进程被视为程序的执行实例,其状态和控制信息通过任务描述符(task_struct)进行管理和维护。本文将深入探讨进程的概念及其关键数据结构task_struct,解析其在操作系统中的作用和实现机制。 ... [详细]
  • Java中高级工程师面试必备:JVM核心知识点全面解析
    对于软件开发人员而言,随着技术框架的不断演进和成熟,许多高级功能已经被高度封装,使得初级开发者只需掌握基本用法即可迅速完成项目。然而,对于中高级工程师而言,深入了解Java虚拟机(JVM)的核心知识点是必不可少的。这不仅有助于优化性能和解决复杂问题,还能在面试中脱颖而出。本文将全面解析JVM的关键概念和技术细节,帮助读者全面提升技术水平。 ... [详细]
  • MySQL性能优化与调参指南【数据库管理】
    本文详细探讨了MySQL数据库的性能优化与参数调整技巧,旨在帮助数据库管理员和开发人员提升系统的运行效率。内容涵盖索引优化、查询优化、配置参数调整等方面,结合实际案例进行深入分析,提供实用的操作建议。此外,还介绍了常见的性能监控工具和方法,助力读者全面掌握MySQL性能优化的核心技能。 ... [详细]
  • 题目描述:小K不幸被LL邪教洗脑,洗脑程度之深使他决定彻底脱离这个邪教。在最终离开前,他计划再进行一次亚瑟王游戏。作为最后一战,他希望这次游戏能够尽善尽美。众所周知,亚瑟王游戏的结果很大程度上取决于运气,但通过合理的策略和算法优化,可以提高获胜的概率。本文将详细解析洛谷P3239 [HNOI2015] 亚瑟王问题,并提供具体的算法实现方法,帮助读者更好地理解和应用相关技术。 ... [详细]
  • 数据结构与算法:HyperLogLog 统计、布隆过滤器应用、缓存机制挑战及解决方案、Redis 性能优化与监控、哨兵模式、版本控制工具 Git
    本文探讨了数据结构与算法在实际应用中的多个方面。首先介绍了HyperLogLog算法,用于高效地进行基数统计,能够准确估算大规模数据集中的唯一元素数量。接着讨论了布隆过滤器的应用,该过滤器在空间效率和查询速度上具有显著优势,适用于大数据场景下的快速成员检测。此外,文章分析了缓存机制面临的挑战及其解决方案,包括LRU和LFU等策略,并详细阐述了Redis的性能优化与监控方法,如使用哨兵模式实现高可用性。最后,介绍了版本控制工具Git的基本操作和最佳实践,帮助开发者有效管理代码版本。 ... [详细]
  • 如何构建和部署C# Windows服务应用程序
    本文介绍了如何从零开始构建和部署C# Windows服务应用程序。通过详细步骤和代码示例,帮助读者掌握创建、配置和部署Windows服务的关键技术点,适合初学者和有经验的开发人员参考。 ... [详细]
  • 浏览器中 W3School JavaScript 的 Location 对象详解
    Location对象是浏览器Window对象的一部分,通过`window.location`属性可访问。它包含了当前页面URL的相关信息,如协议、主机名、路径和查询参数等,对于页面导航和URL操作非常有用。 ... [详细]
  • PyQt5 QTextEdit:深入解析Python中多功能GUI库的应用与实现
    本文详细探讨了 PyQt5 中 QTextEdit 组件在 Python 多功能 GUI 库中的应用与实现。PyQt5 是 Qt 框架的 Python 绑定,提供了超过 620 个类和 6000 个函数及方法,广泛应用于跨平台应用程序开发。QTextEdit 作为其中的重要组件,支持丰富的文本编辑功能,如富文本格式、文本高亮和自定义样式等。PyQt5 的流行性不仅在于其强大的功能,还在于其易用性和灵活性,使其成为开发复杂用户界面的理想选择。 ... [详细]
  • 在2020年8月19日的深度分析中,我们探讨了HTML标签中同时存在`a`标签的`href`和`onclick`属性时的触发顺序问题。此外,还讨论了如何在一个自适应高度的父级`div`中,使两个子`div`中的一个固定高度为300px,另一个自动填充剩余空间的方法。最后,文章详细介绍了JavaScript异步加载的多种实现方式,包括但不限于`async`、`defer`属性以及动态脚本插入技术,为开发者提供了丰富的技术参考。 ... [详细]
  • 在HTML文档中,图像和链接标签的合理应用与优化对于提升网页的用户体验至关重要。本文详细探讨了如何通过正确的语法和属性设置,实现图像和链接的高效展示和功能增强。同时,文章还介绍了常见的优化技巧,如使用alt属性提高图像的可访问性,以及通过rel属性增强链接的安全性和语义性。这些方法不仅有助于搜索引擎优化,还能显著改善用户的浏览体验。 ... [详细]
author-avatar
乌龙小和尚
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有