解析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 自动完成 AutoComplete(Ajax 查询)
Jul 07 Javascript
js 未结束的字符串常量错误解决方法
Jun 13 Javascript
js关闭当前页面(窗口)的几种方式总结
Mar 05 Javascript
JS按字节截取字符长度实例
Nov 20 Javascript
Js中使用hasOwnProperty方法检索ajax响应对象的例子
Dec 08 Javascript
jQuery实现简单二级下拉菜单
Apr 12 Javascript
由ReactJS的Hello world说开来
Jul 02 Javascript
手机端转盘抽奖代码分享
Sep 10 Javascript
AngularJS 使用ng-repeat报错 [ngRepeat:dupes]
Jan 19 Javascript
JS尾递归的实现方法及代码优化技巧
Jan 19 Javascript
layui下拉列表select实现可输入查找的方法
Sep 28 Javascript
在vue中使用cookie记住用户上次选择的实例(本次例子中为下拉框)
Sep 11 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 编写安全的代码时容易犯的错误小结
2010/05/20 PHP
php输出表格的实现代码(修正版)
2010/12/29 PHP
ThinkPHP自动填充实现无限级分类的方法
2014/08/22 PHP
PHP实现获取域名的方法小结
2014/11/05 PHP
php银联网页支付实现方法
2015/03/04 PHP
PHP fclose函数用法总结
2019/02/15 PHP
php使用filter_var函数判断邮箱,url,ip格式示例
2019/07/06 PHP
JavaScript函数模式详解
2014/11/07 Javascript
Nodejs的express使用教程
2015/11/23 NodeJs
Javascript DOM事件操作小结(监听鼠标点击、释放,悬停、离开等)
2017/01/20 Javascript
Vue实现选择城市功能
2017/05/27 Javascript
JS HTML图片显示Canvas 压缩功能
2017/07/21 Javascript
vue-cli开发时,关于ajax跨域的解决方法(推荐)
2018/02/03 Javascript
利用adb shell和node.js实现抖音自动抢红包功能(推荐)
2018/02/22 Javascript
vue使用openlayers实现移动点动画
2020/09/24 Javascript
[02:09]EHOME夺得首届辉夜杯冠军—现场颁奖仪式
2015/12/28 DOTA
[00:48]完美“圣”典2016风云人物:xiao8宣传片
2016/11/30 DOTA
Python的time模块中的常用方法整理
2015/06/18 Python
Python简单实现enum功能的方法
2016/04/25 Python
Python使用修饰器执行函数的参数检查功能示例
2017/09/26 Python
python机器人运动范围问题的解答
2019/04/29 Python
Python-while 计算100以内奇数和的方法
2019/06/11 Python
Python使用正则表达式分割字符串的实现方法
2019/07/16 Python
python的faker库用法
2019/11/28 Python
在Python中利用pickle保存变量的实例
2019/12/30 Python
Python 面向对象之类class和对象基本用法示例
2020/02/02 Python
python 实现非极大值抑制算法(Non-maximum suppression, NMS)
2020/10/15 Python
HTML5 对各个标签的定义与规定:body的介绍
2012/06/21 HTML / CSS
本科生学习总结的自我评价
2013/10/02 职场文书
校园创业策划书
2014/01/14 职场文书
财务部副经理岗位职责
2014/03/14 职场文书
社区义诊活动总结
2014/04/30 职场文书
医师定期考核实施方案
2014/05/07 职场文书
毕业论文致谢部分怎么写
2015/05/14 职场文书
Vue + iView实现Excel上传功能的完整代码
2021/06/22 Vue.js
总结几个非常实用的Python库
2021/06/26 Python