解析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得到网页中所有的div的id
Oct 19 Javascript
纯JS实现动态时间显示代码
Feb 08 Javascript
Node.js入门教程:在windows和Linux上安装配置Node.js图文教程
Aug 14 Javascript
Javascript常用字符串判断函数代码分享
Dec 08 Javascript
jQuery实现dialog设置focus焦点的方法
Jun 10 Javascript
JS插件overlib用法实例详解
Dec 26 Javascript
学习javascript文件加载优化
Feb 19 Javascript
简单实现JS计算器功能
Dec 21 Javascript
bootstrap弹出层的多种触发方式
May 10 Javascript
js Date()日期函数浏览器兼容问题解决方法
Sep 12 Javascript
JS简单获得节点元素的方法示例
Feb 10 Javascript
layui导出所有数据的例子
Sep 10 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中使用$_REQUEST需要注意的一个问题
2013/05/02 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(五)
2014/06/23 PHP
PHP模板引擎Smarty内建函数section,sectionelse用法详解
2016/04/11 PHP
PHP封装的MSSql操作类完整实例
2016/05/26 PHP
ajax调用返回php接口返回json数据的方法(必看篇)
2017/05/05 PHP
PHP unset函数原理及使用方法解析
2020/08/14 PHP
ExtJS 2.0 实用简明教程之布局概述
2009/04/29 Javascript
javascript转换字符串为dom对象(字符串动态创建dom)
2010/05/10 Javascript
基于jQuery的Spin Button自定义文本框数值自增或自减
2010/07/17 Javascript
原生js实现复制对象、扩展对象 类似jquery中的extend()方法
2014/08/30 Javascript
Jquery实现地铁线路指示灯提示牌效果的方法
2015/03/02 Javascript
JS随机调用指定函数的方法
2015/07/01 Javascript
VueJS组件之间通过props交互及验证的方式
2017/09/04 Javascript
详解基于Koa2开发微信二维码扫码支付相关流程
2018/05/16 Javascript
JS实现textarea通过换行或者回车把多行数字分割成数组并且去掉数组中空的值
2018/10/29 Javascript
JS 音频可视化插件Wavesurfer.js的使用教程
2018/10/31 Javascript
vue开发简单上传图片功能
2020/06/30 Javascript
vue通过接口直接下载java生成好的Excel表格案例
2020/10/26 Javascript
[46:37]LGD vs TNC 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/16 DOTA
在 Python 应用中使用 MongoDB的方法
2017/01/05 Python
Python中logging.NullHandler 的使用教程
2018/11/29 Python
浅谈python 读excel数值为浮点型的问题
2018/12/25 Python
Python3.5 Json与pickle实现数据序列化与反序列化操作示例
2019/04/29 Python
Python中join()函数多种操作代码实例
2020/01/13 Python
使用Python获取当前工作目录和执行命令的位置
2020/03/09 Python
纯CSS3打造属于自己的“小黄人”
2016/03/14 HTML / CSS
canvas绘制树形结构可视图形的实现
2020/04/03 HTML / CSS
SteelSeries赛睿官网:游戏外设和配件的领先制造商(耳机、键盘、鼠标和鼠标垫)
2018/06/17 全球购物
Hotels.com英国:全球领先的酒店住宿提供商
2019/01/24 全球购物
党员群众路线整改措施及今后努力方向
2014/10/28 职场文书
2014乡党委副书记党建工作汇报材料
2014/11/02 职场文书
2014年少先队工作总结
2014/12/03 职场文书
2015自愿离婚协议书范本
2015/01/28 职场文书
办公室主任个人总结
2015/02/28 职场文书
办公室禁烟通知
2015/04/23 职场文书
《平移和旋转》教学反思
2016/02/19 职场文书