解析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 当前日期加(天、周、月、年)
Aug 09 Javascript
Jquery AJAX 框架的使用方法
Nov 03 Javascript
jQuery学习笔记之DOM对象和jQuery对象
Dec 22 Javascript
面向对象的Javascript之一(初识Javascript)
Jan 20 Javascript
基于BootStrap的图片轮播效果展示实例代码
May 23 Javascript
浅谈javascript中的constructor
Jun 08 Javascript
用JavaScript获取页面文档内容的实现代码
Jun 10 Javascript
通过修改360抢票的刷新频率和突破8车次限制实现方法
Jan 04 Javascript
如何制作幻灯片(代码分享)
Jan 06 Javascript
jQuery 实现左右两侧菜单添加、移除功能
Jan 02 jQuery
vue路由组件按需加载的几种方法小结
Jul 12 Javascript
浅谈JavaScript浅拷贝和深拷贝
Nov 07 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
动态新闻发布的实现及其技巧
2006/10/09 PHP
ubuntu10.04配置 nginx+php-fpm模式的详解
2013/06/03 PHP
php中strtotime函数性能分析
2016/11/20 PHP
javascript不同页面传值的改进版
2008/09/30 Javascript
ExtJS 2.2.1的grid控件在ie6中的显示问题
2009/05/04 Javascript
js textarea自动增高并隐藏滚动条
2009/12/16 Javascript
javascript 嵌套的函数(作用域链)
2010/03/15 Javascript
DOM Scripting中的图片切换[兼容Firefox]
2010/06/12 Javascript
jquery 新浪网易的评论块制作
2010/07/01 Javascript
JavaScript具有类似Lambda表达式编程能力的代码(改进版)
2010/09/14 Javascript
Angularjs基础知识及示例汇总
2015/01/22 Javascript
JQuery实现展开关闭层的方法
2015/02/17 Javascript
jquery实现的V字形显示效果代码
2015/10/27 Javascript
jQuery延迟执行的实现方法
2016/12/21 Javascript
JS document内容及样式操作完整示例
2020/01/14 Javascript
jQuery实现动态加载瀑布流
2020/09/01 jQuery
vue+axios 拦截器实现统一token的案例
2020/09/11 Javascript
[57:22]2018DOTA2亚洲邀请赛 4.7总决赛 LGD vs Mineski 第五场
2018/04/10 DOTA
编写Python脚本批量下载DesktopNexus壁纸的教程
2015/05/06 Python
Python cookbook(数据结构与算法)找到最大或最小的N个元素实现方法示例
2018/02/13 Python
使用Python中的reduce()函数求积的实例
2019/06/28 Python
python科学计算之scipy——optimize用法
2019/11/25 Python
pytorch使用 to 进行类型转换方式
2020/01/08 Python
python pymysql库的常用操作
2020/10/16 Python
BONIA波尼亚新加坡官网:皮革手袋,鞋类和配件
2016/08/25 全球购物
英国家用电器购物网站:Hughes
2018/02/23 全球购物
惠普香港官方商店:HP香港
2019/04/30 全球购物
利用异或运算实现两个无符号数的加法运算
2013/12/20 面试题
JAVA程序员面试题
2012/10/03 面试题
师范应届毕业生自荐信
2013/11/18 职场文书
决心书范文
2014/03/11 职场文书
人大调研汇报材料
2014/08/14 职场文书
社区母亲节活动总结
2015/02/10 职场文书
2015新学期开学寄语
2015/02/26 职场文书
《老人与海鸥》教学反思
2016/02/16 职场文书
Nginx限流和黑名单配置
2022/05/20 Servers