解析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分页函数代码
Sep 10 Javascript
jquery多行滚动/向左或向上滚动/响应鼠标实现思路及代码
Jan 23 Javascript
javascript时间函数基础介绍
Mar 28 Javascript
jQuery实现Tab菜单滚动切换的方法
Sep 21 Javascript
javascript模块化简单解析
Apr 07 Javascript
JavaScript中的继承之类继承
May 01 Javascript
bootstrap布局中input输入框右侧图标点击功能
May 16 Javascript
Bootstrap树形菜单插件TreeView.js使用方法详解
Nov 01 Javascript
JS高仿抛物线加入购物车特效实现代码
Feb 20 Javascript
node.js基于dgram数据报模块创建UDP服务器和客户端操作示例
Feb 12 Javascript
原生JS实现音乐播放器的示例代码
Feb 25 Javascript
MutationObserver在页面水印实现起到的作用详解
Jul 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 程序员应该使用的10个组件
2009/10/31 PHP
处理单名多值表单的详解
2013/06/08 PHP
php的zip解压缩类pclzip使用示例
2014/03/14 PHP
改写ThinkPHP的U方法使其路由下分页正常
2014/07/02 PHP
php自定义函数实现二维数组按指定key排序的方法
2016/09/29 PHP
PHP常用算法和数据结构示例(必看篇)
2017/03/15 PHP
万能的php分页类
2017/07/06 PHP
php的优点总结 php有哪些优点
2019/07/19 PHP
实现JavaScript中继承的三种方式
2009/10/16 Javascript
Span元素的width属性无效果原因及解决方案
2010/01/15 Javascript
Jquery公告滚动+AJAX后台得到数据
2011/04/14 Javascript
JS Pro-深入面向对象的程序设计之继承的详解
2013/05/07 Javascript
javascript动态判断html元素并执行不同的操作
2014/06/16 Javascript
jQuery使用toggleClass方法动态添加删除Class样式的方法
2015/03/26 Javascript
原生JavaScript实现瀑布流布局
2020/06/28 Javascript
JS动态创建元素的两种方法
2016/04/20 Javascript
浅析在javascript中创建对象的各种模式
2016/05/06 Javascript
浅谈Vue 数据响应式原理
2018/05/07 Javascript
Smartour 让网页导览变得更简单(推荐)
2019/07/19 Javascript
解决Nuxt使用axios跨域问题
2020/07/06 Javascript
浅谈JavaScript中this的指向问题
2020/07/28 Javascript
vue中使用router全局守卫实现页面拦截的示例
2020/10/23 Javascript
Python Sleep休眠函数使用简单实例
2015/02/02 Python
浅析Python 引号、注释、字符串
2019/07/25 Python
Django ORM 查询管理器源码解析
2019/08/05 Python
Pytorch 搭建分类回归神经网络并用GPU进行加速的例子
2020/01/09 Python
Scrapy框架介绍之Puppeteer渲染的使用
2020/06/19 Python
Python 字符串池化的前提
2020/07/03 Python
浅析Python 简单工厂模式和工厂方法模式的优缺点
2020/07/13 Python
html5读取本地文件示例代码
2014/04/22 HTML / CSS
微软日本官方网站:Microsoft日本
2017/11/26 全球购物
澳大利亚100%丝绸多彩度假装商店:TheSwankStore
2019/09/04 全球购物
八一演出活动方案
2014/02/03 职场文书
《动手做做看》教学反思
2014/04/09 职场文书
倡议书格式模板
2014/05/13 职场文书
保护环境建议书400字
2014/05/13 职场文书