解析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 相关文章推荐
利用javascript移动div层-javascript 拖动层
Mar 22 Javascript
jquery配合css简单实现返回顶部效果
Sep 30 Javascript
使用javascript实现有效时间的控制,并显示将要过期的时间
Jan 02 Javascript
基于js与flash实现的网站flv视频播放插件代码
Oct 14 Javascript
JS交换变量的方法
Jan 21 Javascript
JavaScript中Cookies的相关使用教程
Jun 04 Javascript
jQuery日期范围选择器附源码下载
May 23 jQuery
微信小程序列表渲染功能之列表下拉刷新及上拉加载的实现方法分析
Nov 27 Javascript
小程序云开发初探(小结)
Oct 24 Javascript
JS获取今天是本月第几周、本月共几周、本月有多少天、是今年的第几周、是今年的第几天的示例代码
Dec 05 Javascript
微信小程序实现消息框弹出动画
Apr 18 Javascript
微信小程序动画组件使用解析,类似vue,且更强大
Aug 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.MVC的模板标签系统(一)
2006/09/05 PHP
PHP树的代码,可以嵌套任意层
2006/10/09 PHP
php实现二进制和文本相互转换的方法
2015/04/18 PHP
YII2自动登录Cookie总是失效的解决方法
2017/06/28 PHP
javascript实现unicode和字符的互相转换
2007/07/18 Javascript
jQuery中index()方法用法实例
2014/12/27 Javascript
JQuery基础语法小结
2015/02/27 Javascript
js实现鼠标经过表格行变色的方法
2015/05/12 Javascript
浅谈jQuery页面的滚动位置scrollTop、scrollLeft
2015/05/19 Javascript
原生JS实现层叠轮播图
2017/05/17 Javascript
详谈表单格式化插件jquery.serializeJSON
2017/06/23 jQuery
react-router4 嵌套路由的使用方法
2017/07/24 Javascript
Spring Boot/VUE中路由传递参数的实现代码
2018/03/02 Javascript
使用live-server快速搭建本地服务器+自动刷新的方法
2018/03/09 Javascript
使用Angular CLI快速创建Angular项目的一些基本概念和写法小结
2018/04/22 Javascript
详解vue beforeRouteEnter 异步获取数据给实例问题
2019/08/09 Javascript
JavaScript 作用域scope简单汇总
2019/10/23 Javascript
Python实现把数字转换成中文
2015/06/29 Python
用Python编写简单的微博爬虫
2016/03/04 Python
Python如何实现文本转语音
2016/08/08 Python
小白如何入门Python? 制作一个网站为例
2018/03/06 Python
python调用百度REST API实现语音识别
2018/08/30 Python
对Python之gzip文件读写的方法详解
2019/02/08 Python
Python实现操纵控制windows注册表的方法分析
2019/05/24 Python
python 求某条线上特定x值或y值的点坐标方法
2019/07/09 Python
详解Python Matplotlib解决绘图X轴值不按数组排序问题
2019/08/05 Python
python使用正则表达式(Regular Expression)方法超详细
2019/12/30 Python
Python3的socket使用方法详解
2020/02/18 Python
加拿大城市本地限时优惠:Buytopia.ca
2018/09/19 全球购物
澳大利亚波西米亚风情网上商店:Czarina
2019/03/18 全球购物
澳大利亚最大的护发和护肤品购物网站:RY
2019/12/26 全球购物
提拔干部考察材料
2014/05/26 职场文书
班训口号大全
2014/06/18 职场文书
食品科学与工程专业毕业生求职信范文
2014/07/21 职场文书
机械设备与数控技术专业求职信
2014/08/10 职场文书
湖南省召开党的群众路线教育实践活动总结大会报告
2014/10/21 职场文书