前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NodeJS的异常捕获

NodeJS的异常捕获

作者头像
码客说
发布2021-09-01 12:04:02
5.8K0
发布2021-09-01 12:04:02
举报
文章被收录于专栏:码客

正文

由于nodejs是非阻塞单进程单线程的,一旦nodejs抛出异常,整个服务就会停掉。服务将会非常不稳定。

错误异常有两种场景的出现,

  • 一种是代码运行中throw new error没有被捕获
  • 另一种是Promise的失败回调函数,没有对应的reject回调函数处理

针对这两种情况Nodejs都有默认的统一处理方式,就是给整个进程process对象监听相应的错误事件。

代码语言:javascript
复制
process.on('uncaughtException',function(err){
    console.error('未捕获的异常', err.message);
})

process.on('unhandledRejection', function (err, promise) {
    console.error('有Promise没有被捕获的失败函数', err.message);
})

在有可能出现异常的地方,全部使用try { } catch(){ }进行嵌套。

注意

一般情况下,我们会将有可能出错的代码放到 try/catch 块里。 但是到了 Node.js,由于 try/catch 无法捕捉异步回调里的异常,Node.js 原生提供 uncaughtException 事件挂到 process 对象上,用于捕获所有未处理的异常。

处理异常的方式

总的来说处理异常有两种方式

  • 同步异常用try/catch
  • 异步异常要用各自的处理方式

模拟异常

代码语言:javascript
复制
// 每秒钟打印一次时间,确保程序没有奔溃
(function loop() {
    console.log(new Date().getTime())
    setTimeout(function () {
        loop() 
    }, 1000)
})()
// 模拟同步代码块内出现异常
let syncError = () => {
    throw new Error('Sync Error')
}
// 模拟异步代码块内出现异常
let asyncError = () => {
    setTimeout(function () {
        throw new Error('Async Error')
    }, 100)
}

同步异常

try catch 方式

代码语言:javascript
复制
try {
    syncError()
} catch (e) {
    /*处理异常*/
    console.log(e.message)
}
console.log('异常被捕获了,我可以继续执行')

但是try catch方式无法处理异步代码块内出现的异常,你可以理解为执行catch时,异常还没有发生。

代码语言:javascript
复制
try {
    asyncError()
} catch (e) {
    /*异常无法被捕获,导致进程退出*/
    console.log(e.message)
}

异步异常

callback方式

代码语言:javascript
复制
require("fs").mkdir('G://test', function (e) {
  if (e) {
    /*处理异常*/
    console.log(e.message)
  } else {
    console.log('创建目录成功')
  }
})

event方式

代码语言:javascript
复制
let events = require("events");
//创建一个事件监听对象
let emitter = new events.EventEmitter();
//监听error事件
emitter.addListener("error", function (e) {
  /*处理异常*/
  console.log(e.message)
});
//触发error事件
emitter.emit("error", new Error('出错啦'));

Promise 方式

代码语言:javascript
复制
new Promise((resolve, reject) => {
    syncError()
  })
  .then(() => {

  })
  .catch((e) => {
    /*处理异常*/
    console.log(e.message)
  })

或者

代码语言:javascript
复制
new Promise((resolve, reject) => {
    try{
        syncError()
    }catch(e){
        reject(e)
    }
})
    .then(() => {
})
    .catch((e) => {
    /*处理异常*/
    console.log(e.message)
})

Promise同样无法处理异步代码块中抛出的异常

代码语言:javascript
复制
new Promise((resolve, reject) => {
    asyncError()
})
    .then(() => {
    //...
})
    .catch((e) => {
    /*异常无法被捕获,导致进程退出*/
    console.log(e.message)
})

Async/Await 方式

Async是基于Promise的,可以用Await等待响应就可以用try/catch来捕获了。

代码语言:javascript
复制
var sleep = function (time) {
    return new Promise(function (resolve, reject) {
        syncError()
    })
};

(async function () {
    try {
        await sleep(100);
    } catch (e) {
        /*处理异常*/
        console.log(e.message)
    }
})()

所有异常

process方式

process方式可以捕获任何异常(不管是同步代码块中的异常还是异步代码块中的异常)

代码语言:javascript
复制
process.on('uncaughtException', function (e) {
    /*处理异常*/
    console.log(e.message)
});

asyncError()
syncError()

Promise未捕获

代码语言:javascript
复制
process.on('unhandledRejection', function (err, promise) {
  console.error('有Promise没有被捕获的失败函数', err.message);
})

new Promise((resolve, reject) => {
    syncError()
  })
  .then(() => {
    //...
  })

Promise中的异步错误

代码语言:javascript
复制
new Promise((resolve, reject) => {
    asyncError()
  })
  .then(() => {
    //...
  })


process.on('uncaughtException', function (e) {
  /*处理异常*/
  console.log("uncaughtException:", e.message)
});

domain方式

官方不推荐使用

https://nodejs.org/api/domain.html

https://nodejs.org/zh-cn/docs/guides/domain-postmortem/

domain模块,把处理多个不同的IO的操作作为一个组。注册事件和回调到domain,当发生一个错误事件或抛出一个错误时,domain对象会被通知,不会丢失上下文环境,也不导致程序错误立即退出,与process.on('uncaughtException')不同。

Domain 模块可分为隐式绑定和显式绑定:

  • 隐式绑定: 把在domain上下文中定义的变量,自动绑定到domain对象
  • 显式绑定: 把不是在domain上下文中定义的变量,以代码的方式绑定到domain对象

方法 & 描述

1

domain.create() 返回一个domain对象。

2

domain.run(function) 在域的上下文运行提供的函数,隐式的绑定了所有的事件分发器,计时器和底层请求。

3

domain.add(emitter) 显式的增加事件

4

domain.remove(emitter) 删除事件。

5

domain.bind(callback) 返回的函数是一个对于所提供的回调函数的包装函数。当调用这个返回的函数时,所有被抛出的错误都会被导向到这个域的 error 事件。

6

domain.intercept(callback) 和 domain.bind(callback) 类似。除了捕捉被抛出的错误外,它还会拦截 Error 对象作为参数传递到这个函数。

7

domain.enter() 进入一个异步调用的上下文,绑定到domain。

8

domain.exit() 退出当前的domain,切换到不同的链的异步调用的上下文中。对应domain.enter()。

9

domain.on(‘error’,function(err){})捕获的错误监听

process方式虽然可以捕获任何类型的异常,但是process太过笨重,除了记录下错误信息,其他地方不适合使用,domain这个也可以处理任何类型异常的模块,显然是一个不错的选择。

代码语言:javascript
复制
let domain = require('domain')
let d = domain.create()
d.on('error', function (e) {
    /*处理异常*/
    console.log(e.message)
})
d.run(asyncError)
d.run(syncError)

或者

代码语言:javascript
复制
let d = require('domain').create();
d.on('error', function (err) {
  console.log(err.stack);
  d.exit();
});
asyncError();
d.enter();

express框架

express作为nodejs比较常用的框架,其实nodejs自己也有一定的异常错误捕获机制

代码语言:javascript
复制
// Express errorHandler
function errorHandler(err, req, res, next) {
    console.error(err.stack);
    res.status(500).json({
        code: 1,
        err: err.message || '500'
    })
}
app.use(errorHandler);
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-08-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 正文
  • 处理异常的方式
    • 同步异常
      • try catch 方式
    • 异步异常
      • callback方式
      • event方式
      • Promise 方式
      • Async/Await 方式
    • 所有异常
      • process方式
      • domain方式
      • express框架
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档