解析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 相关文章推荐
基于jQuery实现的Ajax 验证用户名是否存在的实现代码
Apr 06 Javascript
JavaScript中获取未知对象属性的代码
Apr 27 Javascript
自制的文件上传JS控件可支持IE、chrome、firefox etc
Apr 18 Javascript
js创建对象的方式总结
Jan 10 Javascript
jQuery EasyUI datagrid实现本地分页的方法
Feb 13 Javascript
jQuery实现360°全景拖动展示
Mar 18 Javascript
jQuery提示插件qTip2用法分析(支持ajax及多种样式)
Jun 08 Javascript
js重写方法的简单实现
Jul 10 Javascript
浅谈React Native 中组件的生命周期
Sep 08 Javascript
基于JavaScript中标识符的命名规则介绍
Jan 06 Javascript
npm全局模块卸载及默认安装目录修改方法
May 15 Javascript
Node.js中的child_process模块详解
Jun 08 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实现把数字ID转字母ID
2013/08/12 PHP
ThinkPHP V2.2说明文档没有说明的那些事实例小结
2015/07/01 PHP
php断点续传之文件分割合并详解
2016/12/13 PHP
PHP编程计算文件或数组中单词出现频率的方法
2017/05/22 PHP
js解决弹窗问题实现班级跳转DIV示例
2014/01/06 Javascript
raphael.js绘制中国地图 地图绘制方法
2014/02/12 Javascript
jQuery如何取id有.的值一般的方法是取不到的
2014/04/18 Javascript
JavaScript实现MIPS乘法模拟的方法
2015/04/17 Javascript
JavaScript过滤字符串中的中文与空格方法汇总
2016/03/07 Javascript
AngularJS中指令的四种基本形式实例分析
2016/11/22 Javascript
JavaScript使用delete删除数组元素用法示例【数组长度不变】
2017/01/17 Javascript
js封装成插件的步骤方法
2017/09/11 Javascript
解决vue打包之后静态资源图片失效的问题
2018/02/21 Javascript
详解VUE-地区选择器(V-Distpicker)组件使用心得
2018/05/07 Javascript
解决vue 界面在苹果手机上滑动点击事件等卡顿问题
2018/11/27 Javascript
webpack3里使用uglifyjs压缩js时打包报错的解决
2018/12/13 Javascript
JavaScript常见继承模式实例小结
2019/01/11 Javascript
JS对象属性的检测与获取操作实例分析
2020/03/17 Javascript
[04:59]2018DOTA2亚洲邀请赛 4.7 Mineski夺冠时刻
2018/04/09 DOTA
Python3控制路由器——使用requests重启极路由.py
2016/05/11 Python
python RabbitMQ 使用详细介绍(小结)
2018/11/08 Python
Django工程的分层结构详解
2019/07/18 Python
tensorflow指定CPU与GPU运算的方法实现
2020/04/21 Python
python线程池如何使用
2020/05/28 Python
python按照list中字典的某key去重的示例代码
2020/10/13 Python
Python Selenium操作Cookie的实例方法
2021/02/28 Python
有关HTML5 Video对象的ontimeupdate事件(Chrome上无效)的问题
2013/07/19 HTML / CSS
英国工具中心:UK Tool Centre
2017/07/10 全球购物
策划助理岗位职责
2013/11/18 职场文书
公司拓展活动方案
2014/02/13 职场文书
模具专业毕业推荐信
2014/03/08 职场文书
学校欢迎标语
2014/06/18 职场文书
国庆65周年演讲稿:回首往昔,展望未来
2014/09/21 职场文书
2014年社区居委会主任重阳节讲话稿
2014/09/25 职场文书
企业2014年度工作总结
2014/12/10 职场文书
2015年度物业公司工作总结
2015/04/27 职场文书