node是对错误处理要求比较高的语言,假如对错误处理没有到位可能会形成程序进程退出javascript
错误处理是程序中一个重要的部分,也是判断你的程序是否专业的标准。通常来讲咱们写程序的时候都会选择使用try...catch来进行错误捕获,或者有时候咱们会使用throw进行错误抛出,这是都是经常使用的错误捕获方法。可是咱们在进行node进行开发的时候就会接触到异步过程的中的错误处理。java
咱们知道在node开发的时候会运用到不少第三方的模块,好比咱们常常会使用最大的包管理工具npm,里面下载的包都会放到咱们项目当中的node_modules里面,咱们打开能够看到里面包含的文件不少,代码量也是巨大的。这里面就会有不少的bug隐患在里面,这时候使用错误捕获就很是有用了。node
其实咱们一开始想到的就是在全局范围内进行错误的监听,node提供了一个uncaughtException捕获异常,可是这种方法咱们会难以定位到错误的发生位置。不该该把该函数当成万能的捕获模块,而是最后的解决方案。web
Error定义了Node中常见的错误类型,咱们可使用Error进行错误的抛出。Error模块里面包含了一个堆栈轨迹用于描述Error是从哪里产生的,通常来讲咱们能够准确知道错误发生在哪一部分的代码当中,根据错误的描述信息能够快速定位到错误。npm
var fs = require("fs");
fs.readFile("file",function(err,data){
if(err){
throw new Error("Error!")
}
})
复制代码
Node程序中产生的全部Error都是使用Error类的实例或者继承自Error类。咱们在程序代码不中不只可使用回调函数自带的Error模块,并且咱们能够显示第捕获错误。好比当你知道逻辑代码运行都某一部分是不对的,应该进行错误的捕获和提醒,你就可使用:json
throw new Error("自定义错误信息!")
复制代码
接下来就简单介绍一下Node中咱们是如何进行错误捕获的,总的来讲咱们能够有如下三种方式,try/catch、callback、event。以前咱们经常使用的try/catch方式只适用于同步的调用状况,可是咱们知道node中会出现不少的异步调用方式。promise
首先咱们应该了解的是在异步操做当中该方法是没法捕获错误的,主要缘由就是由于异步调用返回时,代码的上下文已经改变,回调函数当中的代码已经脱离了try/catch的范围,因此是没法捕获的。dom
同步调用状况:异步
//这里能够捕获
try{
throw new Error("这里出错了!");
}catch(e){
console.log(e)
}
复制代码
异步调用状况:async
try{
setTimeout(function(){throw new Error('这里出错了!')},1000)
}catch(e){
console.log(e);//这里没法进行捕获
}
复制代码
回调函数的方式主要是经过参数的判断来肯定的,node中一般回调函数都会接受两个参数error和result。这两个值确定会有一个不为空,咱们经过读取本地文件的操做来举一个例子。(由于方法返回的是buffer对象难以阅读,咱们就是使用utf8进行读取,最后字符串转成json)
var fs = require("fs");
fs.readFile("./a.json",'utf8', function(error, result) {
if (error) {
console.log(error);
return;
}
console.log(JSON.parse(result));
});
复制代码
假如文件存在就会返回输出结果,故意写成a1.json不存在就会抛出错误:
{ [Error: ENOENT: no such file or directory, open 'D:\test\a1.json']
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'D:\\test\\a1.json' }
复制代码
咱们进行对文件流监听的时候,即便文件流读取是一个同步的方法,可是咱们依旧不能使用try/catch来捕获,为何呢?由于该方法返回了一个对象,只能使用事件处理的方式来处理异常。若是使用try/catch的话直接报错退出,使用事件监听的方式就不会影响程序的运行且会报出错误信息。
因此正确的方式应该是这样的:
var fs = require("fs");
var stream = fs.createReadStream('./a.json');
stream.on("error",function(err){
console.log(err)
})
复制代码
domain模块视图在一个更高的维度上面解决以上提到的三种错误(可处理callback与event形式),可是如今这个模块已是不推荐使用了。首先它的出发点就是把不一样的处理方式统一到这个模块里面监听和捕获。
它的用法是使用了create方法进行建立Domain对象,而后经过Domain对象监听某对象的error事件,且定义好了相应的处理逻辑,最后使用run方法来启动整个Domain,run方法里面的内容就是咱们准备监听的代码。
//处理callback
var fs = require("fs");
var domain = require("domain");
var d = domain.create();
d.run(function(){
fs.readFile('./a1.json','utf8',function(err,data){
if(err){
throw new Error('error')
}
console.log(data)
})
})
d.on('error',function(err){
console.log(err)
})
复制代码
//处理event
var fs = require("fs");
var domain = require("domain");
var d = domain.create();
d.run(function(){
fs.createReadStream('./a1.json')
})
d.on('error',function(err){
console.log(err)
})
复制代码
除此以外,domain能够支持手动调用add方法把对象添加到监听列表当中。因而可知,Domain其实就是将须要管理的对象包裹起来而后经过run与add方法进行处理和实现。可是若是咱们想要把整一个web服务监听的话就是把全部代码都放到run方法里面,可能会形成内存泄露,并且手动调用add方法很难以接受,假如对象被遗漏就可能会花费不少时间进行错误排查。
它的原理其实很简单:
ES6咱们在工做中用的比较多,好比咱们经常使用的就有promise对象了,还有async/await的形式,被称为是异步的终极解决方案。因此咱们也来谈一下ES6中如何进行错误处理。
首先第一个确定是promise了,由于这对于回调函数的操做很友好,避免了一些回调地狱的产生,也提供了try/catch的形式捕获异常。
var promise = new Promise((resolve,reject){
throw new Error("出错啦!")
})
promise.catch(function(error){
console.log(error);
})
复制代码
Generator与async
可使用try/catch语句进行错误捕获,当yield后面的异步操做发生了错误,同样可使用try语句进行捕获。
function * generator(){
try{
yield asyncFunction();
}catch(e){
console,log(e)
}
return 'end'
}
复制代码
假如咱们使用async的形式来写(其实就是语法糖,本质同样),也可使用try/catch来捕获。假如await内部操做出错则后续代码不会执行,可以使用try进行包裹。
async function test(){
try{
await asyncFunction()
}catch(e){
console.log(e)
}
}
复制代码
以上咱们介绍了如何在异步的世界里面进行错误的捕获,以前咱们进行的代码编写都是在同步的世界里,使用try/catch就能够解决大部分你的问题。可是近年来咱们出现了Node,同步的世界被打破了,因此咱们也有必要学习一下如何进行错误的捕获。
上面咱们说了使用原始的方法try/catch、callback回调函数、事件触发机制三种方法。
假如咱们遇到一些不可避免的错误,致使系统崩溃或者程序得不到正常运行,咱们仍是有最后的解决方法而且大部分是有效的,那就是:重启试试!