解析Node.js异常处理中domain模块的使用方法


Posted in Javascript onFebruary 16, 2016

NodeJS 提供了 domain 模块,可以简化异步代码的异常处理。在介绍该模块之前,我们需要首先理解“域”的概念。简单的讲,一个域就是一个 JS 运行环境,在一个运行环境中,如果一个异常没有被捕获,将作为一个全局异常被抛出。NodeJS 通过 process 对象提供了捕获全局异常的方法,示例代码如下

process.on('uncaughtException', function (err) {
  console.log('Error: %s', err.message);
});

setTimeout(function (fn) {
  fn();
});
Error: undefined is not a function

虽然全局异常有个地方可以捕获了,但是对于大多数异常,我们希望尽早捕获,并根据结果决定代码的执行路径。我们用以下 HTTP 服务器代码作为例子:

function async(request, callback) {
  // Do something.
  asyncA(request, function (err, data) {
    if (err) {
      callback(err);
    } else {
      // Do something
      asyncB(request, function (err, data) {
        if (err) {
          callback(err);
        } else {
          // Do something
          asyncC(request, function (err, data) {
            if (err) {
              callback(err);
            } else {
              // Do something
              callback(null, data);
            }
          });
        }
      });
    }
  });
}

http.createServer(function (request, response) {
  async(request, function (err, data) {
    if (err) {
      response.writeHead(500);
      response.end();
    } else {
      response.writeHead(200);
      response.end(data);
    }
  });
});

以上代码将请求对象交给异步函数处理后,再根据处理结果返回响应。这里采用了使用回调函数传递异常的方案,因此 async 函数内部如果再多几个异步函数调用的话,代码就变成上边这副鬼样子了。为了让代码好看点,我们可以在每处理一个请求时,使用 domain 模块创建一个子域(JS 子运行环境)。在子域内运行的代码可以随意抛出异常,而这些异常可以通过子域对象的 error 事件统一捕获。于是以上代码可以做如下改造:

function async(request, callback) {
  // Do something.
  asyncA(request, function (data) {
    // Do something
    asyncB(request, function (data) {
      // Do something
      asyncC(request, function (data) {
        // Do something
        callback(data);
      });
    });
  });
}

http.createServer(function (request, response) {
  var d = domain.create();

  d.on('error', function () {
    response.writeHead(500);
    response.end();
  });

  d.run(function () {
    async(request, function (data) {
      response.writeHead(200);
      response.end(data);
    });
  });
});

可以看到,我们使用.create方法创建了一个子域对象,并通过.run方法进入需要在子域中运行的代码的入口点。而位于子域中的异步函数回调函数由于不再需要捕获异常,代码一下子瘦身很多。

陷阱
无论是通过 process 对象的 uncaughtException 事件捕获到全局异常,还是通过子域对象的 error 事件捕获到了子域异常,在 NodeJS 官方文档里都强烈建议处理完异常后立即重启程序,而不是让程序继续运行。按照官方文档的说法,发生异常后的程序处于一个不确定的运行状态,如果不立即退出的话,程序可能会发生严重内存泄漏,也可能表现得很奇怪。

但这里需要澄清一些事实。JS 本身的throw..try..catch异常处理机制并不会导致内存泄漏,也不会让程序的执行结果出乎意料,但 NodeJS 并不是存粹的 JS。NodeJS 里大量的 API 内部是用 C/C++ 实现的,因此 NodeJS 程序的运行过程中,代码执行路径穿梭于 JS 引擎内部和外部,而 JS 的异常抛出机制可能会打断正常的代码执行流程,导致 C/C++ 部分的代码表现异常,进而导致内存泄漏等问题。

因此,使用 uncaughtException 或 domain 捕获异常,代码执行路径里涉及到了 C/C++ 部分的代码时,如果不能确定是否会导致内存泄漏等问题,最好在处理完异常后重启程序比较妥当。而使用 try 语句捕获异常时一般捕获到的都是 JS 本身的异常,不用担心上诉问题。

Javascript 相关文章推荐
javascript应用:Iframe自适应其加载的内容高度
Apr 10 Javascript
jQuery 性能优化指南(2)
May 21 Javascript
ajax更新数据后,jquery、jq失效问题
Mar 16 Javascript
node.js中使用node-schedule实现定时任务实例
Jun 03 Javascript
javascript字符串函数汇总
Dec 06 Javascript
js实现div在页面拖动效果
May 04 Javascript
jquery 追加元素append、prepend、before、after用法与区别分析
Dec 02 Javascript
微信小程序使用audio组件播放音乐功能示例【附源码下载】
Dec 08 Javascript
微信小程序实现全局搜索代码高亮的示例
Mar 30 Javascript
karma+webpack搭建vue单元测试环境的方法示例
May 24 Javascript
解决微信小程序云开发中获取数据库的内容为空的方法
May 15 Javascript
微信小程序实现图片翻转效果的实例代码
Sep 20 Javascript
jQuery Timelinr实现垂直水平时间轴插件(附源码下载)
Feb 16 #Javascript
深入浅析AngularJS和DataModel
Feb 16 #Javascript
Javascript中的Prototype到底是什么
Feb 16 #Javascript
剖析Node.js异步编程中的回调与代码设计模式
Feb 16 #Javascript
使用Node.js处理前端代码文件的编码问题
Feb 16 #Javascript
让图片跳跃起来  javascript图片轮播特效
Feb 16 #Javascript
Node.js本地文件操作之文件拷贝与目录遍历的方法
Feb 16 #Javascript
You might like
理解php Hash函数,增强密码安全
2011/02/25 PHP
基于php-fpm 参数的深入理解
2013/06/03 PHP
YII Framework框架教程之日志用法详解
2016/03/14 PHP
详谈symfony window下的安装 安装时候出现的问题以及解决方法
2017/09/28 PHP
Javascript中暂停功能的实现代码
2007/03/04 Javascript
JSONP 跨域共享信息
2012/08/16 Javascript
改变文件域的样式实现思路同时兼容ie、firefox
2013/10/23 Javascript
jQuery图片切换插件jquery.cycle.js使用示例
2014/06/16 Javascript
详解JavaScript函数
2015/12/01 Javascript
VueJS事件处理器v-on的使用方法
2017/09/27 Javascript
AngularJS标签页tab选项卡切换功能经典实例详解
2018/05/16 Javascript
Vue.js实现数据响应的方法
2018/08/13 Javascript
使用angularjs.foreach时return的问题解决
2018/09/30 Javascript
JS实现带阴历的日历功能详解
2019/01/24 Javascript
详解nvm管理多版本node踩坑
2019/07/26 Javascript
vue如何在用户要关闭当前网页时弹出提示的实现
2020/05/31 Javascript
[01:20:37]FNATIC vs NIP 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/19 DOTA
python获取标准北京时间的方法
2015/03/24 Python
Python装饰器入门学习教程(九步学习)
2016/01/28 Python
shelve  用来持久化任意的Python对象实例代码
2016/10/12 Python
Python引用传值概念与用法实例小结
2017/10/07 Python
Python中使用支持向量机SVM实践
2017/12/27 Python
Python 常用日期处理 -- calendar 与 dateutil 模块的使用
2020/09/02 Python
windows+vscode安装paddleOCR运行环境的步骤
2020/11/11 Python
解决selenium+Headless Chrome实现不弹出浏览器自动化登录的问题
2021/01/09 Python
CSS3 分类菜单效果
2019/05/27 HTML / CSS
Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
2015/01/27 面试题
幼儿园中秋节活动方案2013
2014/01/29 职场文书
解除劳动合同协议书
2014/04/14 职场文书
护士求职信范文
2014/05/24 职场文书
运动会加油稿
2015/07/22 职场文书
如何利用STAR法则制作留学文书?
2019/08/26 职场文书
Django给表单添加honeypot验证增加安全性
2021/05/06 Python
python之PySide2安装使用及QT Designer UI设计案例教程
2021/07/26 Python
优化Mysql查询的示例
2022/04/26 MySQL
vue递归实现树形组件
2022/07/15 Vue.js