解析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 相关文章推荐
几个高效,简洁的字符处理函数
Apr 12 Javascript
javascript针对DOM的应用分析(三)
Apr 15 Javascript
如何使用json在前后台进行数据传输实例介绍
Apr 11 Javascript
jQuery弹出(alert)select选择的值
Apr 21 Javascript
JavaScript中for-in遍历方式示例介绍
Feb 11 Javascript
基于jquery插件编写countdown计时器
Jun 12 Javascript
详解React 在服务端渲染的实现
Nov 16 Javascript
webpack配置导致字体图标无法显示的解决方法
Mar 06 Javascript
vue父组件给子组件的组件传值provide inject的方法
Oct 23 Javascript
Vue列表如何实现滚动到指定位置样式改变效果
May 09 Javascript
基于JS实现视频上传显示进度条
May 12 Javascript
解决ant-design-vue中menu菜单无法默认展开的问题
Oct 31 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冒泡排序、快速排序、快速查找、二维数组去重实例分享
2014/04/24 PHP
set_exception_handler函数在ThinkPHP中的用法
2014/10/31 PHP
CI配置多数据库访问的方法
2016/03/28 PHP
php自动加载方式集合
2016/04/04 PHP
有关js的变量作用域和this指针的讨论
2010/12/16 Javascript
javascript实现继承的简单实例
2015/07/26 Javascript
JavaScript函数的调用以及参数传递
2015/10/21 Javascript
浏览器复制插件zeroclipboard使用指南
2016/03/26 Javascript
基于BootStrap Metronic开发框架经验小结【六】对话框及提示框的处理和优化
2016/05/12 Javascript
JavaScript实现经典排序算法之选择排序
2016/12/28 Javascript
详解js前端代码异常监控
2017/01/11 Javascript
AngularJS中的缓存使用
2017/01/11 Javascript
JS使用面向对象技术实现的tab选项卡效果示例
2017/02/28 Javascript
Three.js获取鼠标点击的三维坐标示例代码
2017/03/24 Javascript
bootstrap-table.js扩展分页工具栏(增加跳转到xx页)功能
2017/12/28 Javascript
vue-cli项目使用mock数据的方法(借助express)
2019/04/15 Javascript
解决elementui表格操作列自适应列宽
2020/12/28 Javascript
[03:46]DOTA2英雄基础教程 维萨吉
2013/12/11 DOTA
Python 变量类型及命名规则介绍
2013/06/08 Python
python对象及面向对象技术详解
2016/07/19 Python
flask + pymysql操作Mysql数据库的实例
2017/11/13 Python
解决python中使用plot画图,图不显示的问题
2018/07/04 Python
利用Django-environ如何区分不同环境
2018/08/26 Python
Python 数据的累加与统计的示例代码
2020/08/03 Python
解决Pyinstaller打包软件失败的一个坑
2021/03/04 Python
HTML 5.1来了 9月份正式发布 更新内容预览
2016/04/26 HTML / CSS
自1926年以来就为冰岛保持温暖:66°North
2020/11/27 全球购物
运动会领导邀请函
2014/02/05 职场文书
ktv总经理岗位职责
2014/02/17 职场文书
加多宝凉茶广告词
2014/03/18 职场文书
诉讼代理人授权委托书
2014/10/11 职场文书
对党的十八届四中全会的期盼
2014/10/17 职场文书
2016年感恩节寄语
2015/12/07 职场文书
优质护理服务心得体会
2016/01/22 职场文书
2019各种保证书范文
2019/06/24 职场文书
Go语言操作数据库及其常规操作的示例代码
2021/04/21 Golang