解析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类继承机制的原理分析
Sep 12 Javascript
js限制文本框只能输入数字(正则表达式)
Jul 15 Javascript
设置jsf的选择框h:selectOneMenu为不可编辑状态的方法
Jan 07 Javascript
javascript实用方法总结
Feb 06 Javascript
用JavaScript来美化HTML的select标签的下拉列表效果
Nov 17 Javascript
bootstrap布局中input输入框右侧图标点击功能
May 16 Javascript
js实现刷新页面后回到记录时滚动条的位置【两种方案可选】
Dec 12 Javascript
快速实现JS图片懒加载(可视区域加载)示例代码
Jan 04 Javascript
Vue实现typeahead组件功能(非常靠谱)
Aug 26 Javascript
jquery无缝图片轮播组件封装
Nov 25 jQuery
vue 路由子组件created和mounted不起作用的解决方法
Nov 05 Javascript
Javascript幻灯片播放功能实现过程解析
May 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
PHP伪静态页面函数附使用方法
2008/06/20 PHP
全世界最小的php网页木马一枚 附PHP木马的防范方法
2009/10/09 PHP
Notice: Undefined index: page in E:\PHP\test.php on line 14
2010/11/02 PHP
PHP使用glob函数遍历目录或文件夹的方法
2014/12/16 PHP
PHP使用HTML5 FileApi实现Ajax上传文件功能示例
2019/07/01 PHP
js 编程笔记 无名函数
2011/06/28 Javascript
javascript 基础篇2 数据类型,语句,函数
2012/03/14 Javascript
在HTML代码中使用JavaScript代码的例子
2014/10/16 Javascript
基于jQuery创建鼠标悬停效果的方法
2015/03/07 Javascript
JS实现把鼠标放到链接上出现滚动文字的方法
2016/04/06 Javascript
javascript实现不同颜色Tab标签切换效果
2016/04/27 Javascript
jQuery实现点击行选中或取消CheckBox的方法
2016/08/01 Javascript
轻松掌握JavaScript单例模式
2016/08/25 Javascript
Javascript实现图片懒加载插件的方法
2016/10/20 Javascript
windows下vue.js开发环境搭建教程
2017/03/20 Javascript
angularjs定时任务的设置与清除示例
2017/06/02 Javascript
iview table高度动态设置方法
2018/03/14 Javascript
mpvue构建小程序的方法(步骤+地址)
2018/05/22 Javascript
jquery实现选项卡切换代码实例
2019/05/14 jQuery
微信小程序云开发获取文件夹下所有文件(推荐)
2019/11/14 Javascript
[43:32]Winstrike vs VGJ.S 2018国际邀请赛淘汰赛BO3 第一场 8.23
2018/08/24 DOTA
详解Django中的ifequal和ifnotequal标签使用
2015/07/16 Python
一些Centos Python 生产环境的部署命令(推荐)
2018/05/07 Python
Django基础知识与基本应用入门教程
2018/07/20 Python
python爬虫框架scrapy实现模拟登录操作示例
2018/08/02 Python
Python延时操作实现方法示例
2018/08/14 Python
对Python之gzip文件读写的方法详解
2019/02/08 Python
Python 利用高德地图api实现经纬度与地址的批量转换
2019/08/14 Python
python网络编程 使用UDP、TCP协议收发信息详解
2019/08/29 Python
python爬虫scrapy图书分类实例讲解
2020/11/23 Python
大三自我鉴定范文
2013/10/05 职场文书
美术指导求职信
2014/03/17 职场文书
白酒代理协议书范本
2014/10/26 职场文书
2014年大学学生会工作总结
2014/12/02 职场文书
python实现大文本文件分割成多个小文件
2021/04/20 Python
numpy数据类型dtype转换实现
2021/04/24 Python