解析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 相关文章推荐
ASP Json Parser修正版
Dec 06 Javascript
图像替换新技术 状态域方法
Jan 28 Javascript
JavaScript 嵌套函数指向this对象错误的解决方法
Mar 15 Javascript
用Jquery实现滚动新闻
Feb 12 Javascript
2014最热门的JavaScript代码高亮插件推荐
Nov 25 Javascript
无刷新上传文件并返回自定义值
Jun 11 Javascript
学习javascript面向对象 理解javascript对象
Jan 04 Javascript
javascript实现移动端上的触屏拖拽功能
Mar 04 Javascript
jQuery实现Select左右复制移动内容
Aug 05 Javascript
vue.js默认路由不加载linkActiveClass问题的解决方法
Dec 11 Javascript
webpack优化的深入理解
Dec 10 Javascript
微信小程序实现点击空白隐藏的方法示例
Aug 13 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
Js点击弹出下拉菜单效果实例
2013/08/12 Javascript
深入分析escape()、encodeURI()、encodeURIComponent()的区别及示例
2014/08/04 Javascript
JavaScript检查弹出窗口是否被阻拦的方法技巧
2015/03/13 Javascript
js实现同一页面多个运动效果的方法
2015/04/10 Javascript
js实现兼容IE和FF的上下层的移动
2015/05/04 Javascript
jQuery实现漂亮实用的商品图片tips提示框效果(无图片箭头+阴影)
2016/04/16 Javascript
javascript获取网页各种高宽及位置的方法总结
2016/07/27 Javascript
jQuery实现图片轮播效果代码
2016/09/27 Javascript
JavaScript中访问id对象 属性的方式访问属性(实例代码)
2016/10/28 Javascript
JavaScript实现网页头部进度条刷新
2017/04/16 Javascript
ExtJs异步无法向外传值和赋值的完美解决办法
2017/06/14 Javascript
详解nodejs通过代理(proxy)发送http请求(request)
2017/09/22 NodeJs
使用npm安装最新版本nodejs
2018/01/18 NodeJs
js 根据对象数组中的属性进行排序实现代码
2019/09/12 Javascript
jQuery实现简单评论功能
2020/08/19 jQuery
Python中每次处理一个字符的5种方法
2015/05/21 Python
python实现的用于搜索文件并进行内容替换的类实例
2015/06/28 Python
python实现简单登陆流程的方法
2018/04/22 Python
python批量下载网站马拉松照片的完整步骤
2018/12/05 Python
利用pyshp包给shapefile文件添加字段的实例
2019/12/06 Python
Python实现遗传算法(二进制编码)求函数最优值方式
2020/02/11 Python
详解django中Template语言
2020/02/22 Python
python selenium操作cookie的实现
2020/03/18 Python
Python如何在bool函数中取值
2020/09/21 Python
Python实现网络聊天室的示例代码(支持多人聊天与私聊)
2021/01/27 Python
一款纯css3实现的鼠标悬停动画按钮
2014/12/29 HTML / CSS
Foot Locker英国官网:美国知名运动产品零售商
2019/02/21 全球购物
Ray-Ban雷朋瑞典官方网站:全球领先的太阳眼镜品牌
2019/08/22 全球购物
荷兰睡眠专家:Beter Bed
2020/11/23 全球购物
物理系毕业生自荐书范文
2014/02/22 职场文书
金融系毕业生自荐书
2014/07/08 职场文书
高考学习决心书
2015/02/04 职场文书
2015年双拥工作总结
2015/04/08 职场文书
2016重阳节红领巾广播稿
2015/12/18 职场文书
导游词之西递宏村
2019/12/10 职场文书
JS setTimeout与setInterval的区别
2022/04/20 Javascript