解析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 相关文章推荐
js创建对象的几种常用方式小结(推荐)
Oct 24 Javascript
JavaScript中的依赖注入详解
Mar 18 Javascript
关于javascript中限定时间内防止按钮重复点击的思路详解
Aug 16 Javascript
JavaScript实现输入框与清空按钮联动效果
Sep 09 Javascript
BootStrap 弹出层代码
Feb 09 Javascript
Bootstrap3下拉菜单的实现
Feb 22 Javascript
Node.JS中事件轮询(Event Loop)的解析
Feb 25 Javascript
JS实现颜色动态淡化效果
Mar 06 Javascript
基于bootstrap按钮式下拉菜单组件的搜索建议插件
Mar 25 Javascript
BackBone及其实例探究_动力节点Java学院整理
Jul 14 Javascript
jQuery实现form表单基于ajax无刷新提交方法实例代码
Nov 04 jQuery
uni-app使用countdown插件实现倒计时
Nov 01 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上传文件常见问题总结
2015/02/03 PHP
IE8 引入跨站数据获取功能说明
2008/07/22 Javascript
JavaScript中的匀速运动和变速(缓冲)运动详细介绍
2012/11/11 Javascript
javascript中自定义对象的属性方法分享
2013/07/12 Javascript
JS中图片缓冲loading技术的实例代码
2013/08/29 Javascript
js禁止页面复制功能禁用页面右键菜单示例代码
2013/08/29 Javascript
node.js中的url.resolve方法使用说明
2014/12/10 Javascript
javascript中的altKey 和 Event属性大全
2015/11/06 Javascript
javascript动画之模拟拖拽效果篇
2016/09/26 Javascript
Bootstrap 响应式实用工具实例详解
2017/03/29 Javascript
node使用UEditor富文本编辑器的方法实例
2017/07/11 Javascript
Postman环境变量全局变量使用方法详解
2020/08/13 Javascript
js实现碰撞检测
2021/01/29 Javascript
[01:19]DOTA2城市挑战赛报名开始 开启你的城市传奇
2018/03/23 DOTA
Python对象的深拷贝和浅拷贝详解
2014/08/25 Python
Python中扩展包的安装方法详解
2017/06/14 Python
Python多项式回归的实现方法
2019/03/11 Python
Django 拆分model和view的实现方法
2019/08/16 Python
使用python turtle画高达
2020/01/19 Python
Python中过滤字符串列表的方法
2020/12/22 Python
python解决OpenCV在读取显示图片的时候闪退的问题
2021/02/23 Python
英国最大的老式糖果店:A Quarter Of
2017/04/08 全球购物
哥伦比亚加拿大官网:Columbia Sportswear Canada
2020/09/07 全球购物
Java和Javasciprt的区别
2012/09/02 面试题
食品营养与检测应届生求职信
2013/11/08 职场文书
职业教育毕业生求职信
2013/11/09 职场文书
政法学院毕业生求职信
2014/02/28 职场文书
学生社团文化节开幕式主持词
2014/03/28 职场文书
婚纱摄影师求职信范文
2014/04/17 职场文书
提拔干部考察材料
2014/05/26 职场文书
反对形式主义、官僚主义、享乐主义和奢靡之风整改措施
2014/09/17 职场文书
三方股份合作协议书
2014/10/13 职场文书
普宁寺导游词
2015/02/04 职场文书
起诉状范本
2015/05/20 职场文书
恰同学少年观后感
2015/06/08 职场文书
matplotlib画混淆矩阵与正确率曲线的实例代码
2021/06/01 Python