浅谈前端中的错误处理

某一天用户反应翻开的页面白屏幕,怎样定位到发作毛病的缘由呢?一样平常某次宣布怎样肯定宣布会没有引入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每次挪用都在一个沙箱环境下挪用,用完接纳调。

  • 末了就是防止轮回援用了,如许也会致使没法接纳
    原文作者:ixlei
    原文地址: https://segmentfault.com/a/1190000015567273
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞