解析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 设计模式 推荐
Oct 28 Javascript
Safari5中alert的无限循环BUG
Apr 07 Javascript
读jQuery之三(构建选择器)
Jun 11 Javascript
下雪了 javascript实现雪花飞舞
Aug 02 Javascript
三种带箭头提示框总结实例
Jun 14 Javascript
JavaScript订单操作小程序完整版
Jun 23 Javascript
Vue-Cli中自定义过滤器的实现代码
Aug 12 Javascript
Vue使用mixins实现压缩图片代码
Mar 14 Javascript
mpvue构建小程序的方法(步骤+地址)
May 22 Javascript
js 实现在2d平面上画8的方法
Oct 10 Javascript
从零开始在NPM上发布一个Vue组件的方法步骤
Dec 20 Javascript
Vue实现购物小球抛物线的方法实例
Nov 22 Vue.js
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
完美解决:Apache启动问题―(OS 10022)提供了一个无效的参数
2013/06/08 PHP
php+mysqli批量查询多张表数据的方法
2015/01/29 PHP
PHP实现打包zip并下载功能
2018/06/12 PHP
ExtJS实现文件下载的方法实例
2013/11/09 Javascript
js几秒以后倒计时跳转示例
2013/12/26 Javascript
js登录弹出层特效
2014/03/07 Javascript
JS完整获取IE浏览器信息包括类型、版本、语言等等
2014/05/22 Javascript
js实现的捐赠管理完整实例
2015/01/20 Javascript
JQUERY简单按钮轮换选中效果实现方法
2015/05/07 Javascript
jquery简单倒计时实现方法
2015/12/18 Javascript
JS实现部分HTML固定页面顶部随屏滚动效果
2015/12/24 Javascript
javascript实现起伏的水波背景效果
2016/05/16 Javascript
jQuery 获取多选框的值及多选框中文的函数
2016/05/16 Javascript
window.open打开窗口被拦截的快速解决方法
2016/08/04 Javascript
超全面的vue.js使用总结
2017/02/12 Javascript
详解如何使用Node.js编写命令工具——以vue-cli为例
2017/06/29 Javascript
Vue仿手机qq的实例代码(demo)
2017/09/08 Javascript
JS实现提交表单前的数字及邮箱校检功能
2017/11/13 Javascript
基于mpvue的小程序项目搭建的步骤
2018/05/22 Javascript
js实现动态增加文件域表单功能
2018/10/22 Javascript
前端路由&webpack基础配置详解
2019/06/10 Javascript
[10:53]2018DOTA2国际邀请赛寻真——EG
2018/08/11 DOTA
Python字典简介以及用法详解
2016/11/15 Python
Python 文本文件内容批量抽取实例
2018/12/10 Python
浅谈python之高阶函数和匿名函数
2019/03/21 Python
python分布式编程实现过程解析
2019/11/08 Python
Python实现投影法分割图像示例(一)
2020/01/17 Python
Python turtle画图库&&画姓名实例
2020/01/19 Python
Spring http服务远程调用实现过程解析
2020/06/11 Python
纯CSS绘制漂亮的圆形图案效果
2014/05/07 HTML / CSS
Microsoft新加坡官方网站:购买微软最新软件和技术产品
2016/10/28 全球购物
Dr.Jart+美国官网:韩国药妆品牌
2019/01/18 全球购物
文明礼貌演讲稿
2014/05/12 职场文书
建议书格式
2015/02/04 职场文书
幼儿园大班教育随笔
2015/08/14 职场文书
Mysql中调试存储过程最简单的方法
2021/06/30 MySQL