Nodejs异步异常处理domain

前言

程序开发中,最麻烦的事情之一就是异常处理;对于Nodejs程序开发,最麻烦的事情莫过于异步异常处理。

以MVC的多层架构设计角度,异常总是要一层一层向上抛出,最后在客户端出打印错误。但是,Nodejs都是异步异常,try..catch根本就捕捉不到,就会给我们的程序设计带来不小的麻烦,经常会有未处理的runtime异常,让整个系统挂掉。

目录

  1. Nodejs异常处理
  2. Nodejs异步异常处理
  3. domain介绍
  4. domain的API介绍
  5. domain异步异常特例

1. Nodejs同步异常处理

系统环境

  • win7 64bit
  • Nodejs:v0.10.5
  • Npm:1.2.19

创建项目

~ D:\workspace\javascript>mkdir nodejs-domain && cd nodejs-domain

新建文件:sync.js,模拟同步异常的处理

~ vi sync.js

function sync_error() {
    var r = Math.random() * 10;
    console.log("random num is " + r);
    if (r > 5) {
        throw new Error("Error: random num" + r + " > 5");
    }
}

setInterval(function () {
    try {
        sync_error();
    } catch (err) {
        console.log(err);
    }

}, 1000)

运行程序

~ D:\workspace\javascript\nodejs-domain>node sync.js
random num is 1.067440479528159
random num is 6.284254263155162
[Error: Error: random num6.284254263155162 > 5]
random num is 8.445568478200585
[Error: Error: random num8.445568478200585 > 5]
random num is 2.79862868366763
random num is 5.452311446424574
[Error: Error: random num5.452311446424574 > 5]
random num is 3.725348354782909
random num is 7.590636070817709
[Error: Error: random num7.590636070817709 > 5]
random num is 9.584896392188966
[Error: Error: random num9.584896392188966 > 5]
random num is 3.63708304008469
random num is 5.747077965643257
[Error: Error: random num5.747077965643257 > 5]
random num is 1.0771577199921012
random num is 8.898805833887309
[Error: Error: random num8.898805833887309 > 5]
random num is 6.59792885184288
[Error: Error: random num6.59792885184288 > 5]
random num is 1.8532328261062503
random num is 3.6028534593060613
random num is 2.7523675211705267
random num is 1.598257850855589

通过try..catch可以很好的抓到同步程序的异常。

2. Nodejs异步异常处理

新建文件:async.js,模拟异步异常处理

~ vi async.js

function async_error() {
    setTimeout(function(){
        var r = Math.random() * 10;
        console.log("random num is " + r);
        if (r > 5) {
            throw new Error("Error: random num" + r + " > 5");
        }
    },10)

}

setInterval(function () {
    try {
        async_error();
    } catch (err) {
        console.log(err);
    }
}, 1000)

运行程序

~ D:\workspace\javascript\nodejs-domain\sync.js:5
        throw new Error("Error: random num" + r + " > 5");
              ^
Error: Error: random num9.974474618211389 > 5
    at trycatch (D:\workspace\javascript\nodejs-domain\sync.js:5:15)
    at Timer. (D:\workspace\javascript\nodejs-domain\sync.js:10:5)
    at Timer.timer.ontimeout (timers.js:247:14)

try..catch,无法捕捉异步异常!

修改async.js, 通过process.on()打印错误信息。

~ vi async.js

function async_error() {
    setTimeout(function(){
        var r = Math.random() * 10;
        console.log("random num is " + r);
        if (r > 5) {
            throw new Error("Error: random num" + r + " > 5");
        }
    },10)

}

setInterval(function () {
    try {
        async_error();
    } catch (err) {
        console.log(err);
    }
}, 1000)

process.on(‘uncaughtException‘, function (err) {
    console.log(err);
});

运行程序

~ D:\workspace\javascript\nodejs-domain>node async.js
random num is 9.33843155624345
[Error: Error: random num9.33843155624345 > 5]
random num is 7.894433259498328
[Error: Error: random num7.894433259498328 > 5]
random num is 2.532815719023347
random num is 6.0961083066649735
[Error: Error: random num6.0961083066649735 > 5]
random num is 5.138748907484114
[Error: Error: random num5.138748907484114 > 5]

通过process.on(‘uncaughtException’)的内置函数,虽然我们可以记录下这个错误的日志,而且进程也不会异常退出,但是我们是没有办法对发现错误的请求友好返回的,只能够让它超时返回。

3. domain介绍

node在v0.8+版本的时候,发布了一个模块domain。这个模块做的就是try catch所无法做到的:捕捉异步回调中出现的异常。

domain模块,把处理多个不同的IO的操作作为一个组。注册事件和回调到domain,当发生一个错误事件或抛出一个错误时,domain对象 会被通知,不会丢失上下文环境,也不导致程序错误立即推出,与process.on(‘uncaughtException’)不同。

domain的发布页http://nodejs.org/api/domain.html

用domain捕捉异常,新建文件domain.js

~ vi domain.js

var domain = require(‘domain‘);

function sync_error() {
    var r = Math.random() * 10;
    console.log("sync num is " + r);
    if (r > 5) {
        throw new Error("sync: random num" + r + " > 5");
    }
}

function async_error() {
    setTimeout(function(){
        var r = Math.random() * 10;
        console.log("async num is " + r);
        if (r > 5) {
            throw new Error("async: random num" + r + " > 5");
        }
    },10)

}

var d = domain.create();
d.on(‘error‘,function(err){
    console.log(err);
});

setInterval(function () {
    d.run(sync_error);
    d.run(async_error);
}, 1000)

运行程序

~ D:\workspace\javascript\nodejs-domain>node domain.js
sync num is 8.492766928393394
{ [Error: sync: random num8.492766928393394 > 5]
  domain:
   { domain: null,
     _events: { error: [Function] },
     _maxListeners: 10,
     members: [] },
  domainThrown: true }
sync num is 4.991524459328502
async num is 7.5735537661239505
{ [Error: async: random num7.5735537661239505 > 5]
  domain:
   { domain: null,
     _events: { error: [Function] },
     _maxListeners: 10,
     members: [] },
  domainThrown: true }
sync num is 4.626072463579476
async num is 9.48660139227286
{ [Error: async: random num9.48660139227286 > 5]
  domain:
   { domain: null,
     _events: { error: [Function] },
     _maxListeners: 10,
     members: [] },
  domainThrown: true }
sync num is 2.3057156521826982
async num is 4.5645097037777305
sync num is 2.0251641585491598
async num is 7.712894310243428
{ [Error: async: random num7.712894310243428 > 5]
  domain:
   { domain: null,
     _events: { error: [Function] },
     _maxListeners: 10,
     members: [] },
  domainThrown: true }

我们发现domain,可以同时捕捉到同步异常(sync)和异步异常(async)。

4. domain的API介绍

基本概念

  • 隐式绑定: 把在domain上下文中定义的变量,自动绑定到domain对象
  • 显式绑定: 把不是在domain上下文中定义的变量,以代码的方式绑定到domain对象

API介绍

  • domain.create(): 返回一个domain对象
  • domain.run(fn): 在domain上下文中执行一个函数,并隐式绑定所有事件,定时器和低级的请求。
  • domain.members: 已加入domain对象的域定时器和事件发射器的数组。
  • domain.add(emitter): 显式的增加事件
  • domain.remove(emitter): 删除事件
  • domain.bind(callback): 以return为封装callback函数
  • domain.intercept(callback): 同domain.bind,但只返回第一个参数
  • domain.enter(): 进入一个异步调用的上下文,绑定到domain
  • domain.exit(): 退出当前的domain,切换到不同的链的异步调用的上下文中。对应domain.enter()
  • domain.dispose(): 释放一个domain对象,让node进程回收这部分资源

5. domain异步异常特例

下面这个例子,domain将捕捉不到异步异常

var domain = require(‘domain‘);
var EventEmitter = require(‘events‘).EventEmitter;

var e = new EventEmitter();

var timer = setTimeout(function () {
    e.emit(‘data‘);
}, 10);

function next() {
    e.once(‘data‘, function () {
        throw new Error(‘Receive data error!‘);
    });
}

var d = domain.create();
d.on(‘error‘, function (err) {
    console.log(err);
});

d.run(next);

运行程序

~ D:\workspace\javascript\nodejs-domain\special.js:12
        throw new Error(‘Receive data error!‘);
              ^
Error: Receive data error!
    at EventEmitter. (D:\workspace\javascript\nodejs-domain\special.js:12:15)
    at EventEmitter.g (events.js:175:14)
    at EventEmitter.emit (events.js:92:17)
    at null._onTimeout (D:\workspace\javascript\nodejs-domain\special.js:7:7)
    at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)

如果timer和e两个关键的对象在初始化的时候都时没有在domain的范围之内,因此,当在next函数中监听的事件被触发,执行抛出异常的回调函数时,其实根本就没有处于domain的包裹中,就不会被domain捕获到异常了!

修改程序代码

var domain = require(‘domain‘);
var EventEmitter = require(‘events‘).EventEmitter;

var e = new EventEmitter();

var timer = setTimeout(function () {
    e.emit(‘data‘);
}, 10);

function next() {
    e.once(‘data‘, function () {
        throw new Error(‘Receive data error!‘);
    });
}

var d = domain.create();
d.on(‘error‘, function (err) {
    console.log(err);
});

d.add(e);
d.add(timer);

d.run(next);

增加e和timer到domain的范围内,运行程序

~ D:\workspace\javascript\nodejs-domain>node special.js
{ [Error: Receive data error!]
  domain:
   { domain: null,
     _events: { error: [Function] },
     _maxListeners: 10,
     members: [ [Object], [Object] ] },
  domainThrown: true }

domain特例代码摘自:http://cnodejs.org/topic/516b64596d38277306407936

通过domain模块,我们就可以好好设计Nodejs系统的异常管理了。

文章转载自:http://blog.fens.me/nodejs-core-domain/ ‎   看到不错的文章,自己又能看懂的果断转载,学习并收录下,o(∩_∩)o

时间: 2024-10-29 16:58:09

Nodejs异步异常处理domain的相关文章

深入浅出NodeJS——异步编程

函数式编程 Javascript中函数作为一等公民,函数看成普通对象,可以作为参数或者返回值. 高阶函数:函数作为参数或者将函数作为返回值的函数 异步编程优势 基于事件驱动的非阻塞I/O模型 异步编程难点 (1) 异常处理,通常try/catch不一定适用,因为callback并不是在当前Tick中执行. Node在异常处理中约定将异常作为回调函数的第一个实参传回. (2)  函数嵌套过深 (3) 代码阻塞:没有sleep函数,通过setInterval()和setTimeout()模拟 (4)

深入浅出NodeJS——异步I/O

底层操作系统,异步通过信号量.消息等方式有着广泛的应用. PHP语言从头到尾都是以同步阻塞方式运行,利于程序员顺序编写业务逻辑. 异步I/O.事件驱动.单线程构成Node的基调. why异步I/O (1).用户体验 在Web2.0中Ajax广泛应用异步刷新机制可以更好的提高用户体验,消除UI阻塞.后端同样采用异步I/O可以有效较少同时请求多个资源的效应时间其为max(M,N). (2).资源分配 多任务主流方式: a. 单线程异步I/O b. 多线程并行 多线程的代价在于创建线程和执行期线程上下

Nodejs异步回调的优雅处理方法

这篇文章主要介绍了Nodejs异步回调的优雅处理方法,本文使用了ES6中的新特性,用一种十分优雅的方式解决了回调问题,需要的朋友可以参考下 前言 Nodejs最大的亮点就在于事件驱动, 非阻塞I/O 模型,这使得Nodejs具有很强的并发处理能力,非常适合编写网络应用.在Nodejs中大部分的I/O操作几乎都是异步的,也就是我们处理I/O的操 作结果基本上都需要在回调函数中处理,比如下面的这个读取文件内容的函数: 复制代码 代码如下: fs.readFile('/etc/passwd', fun

Node.js之路【第三篇】NodeJS异步实现

NodeJS异步实现 Node.js异步编程的直接体现就是回调,它依托于回调来实现,但不能说使用了回调他就是异步了 回调函数在完成任务后就会被调用,Node使用了大量的回调函数,Node所有的API都支持回调函数 例如:我们可以一边读取文件一边执行其他命令,在文件读取完成后,我们将文件内容作为回调的参数返回,这样执行代码的时候就不会有阻塞或等待I/O操作 这样就打打提高了Node.js性能,可以处理大量的并发请求. 一.阻塞代码示例 1.创建一个测试文件text.txt文件内容如下: 文件I/O

NodeJS之异常处理

1. 为什么要处理异常? 如果我们不处理异常的话,直接会导致程序奔溃,用户体验比较差,因此我们要对异常进行处理,当出现异常的情况下,我们要给用户一个友好的提示,并且记录该异常,方便我们排查. 2. 在Node.js中常用的异常处理方式有哪些? 2.1 使用try catch方式来处理异常,如下代码: try { throw new Error('error'); } catch(e) { console.log('异常被捕获了,我现在还可以继续执行了'); console.log(e); } 然

nodejs异步---Async

一:异步基础 1.由于node.js天生就是异步,所以效率很高,性能强. console.log('hi!'); setTimeout(function(){ console.log('hello!'); },1000); console.log('wow!'); 比如这个,输出结果:hi wow! hello! 可以看出nodejs的异步性 2.高阶函数 高阶函数给我的感觉就是闭包. function test(a){ return function(b){ return a+b; } } v

使用async解决nodejs异步问题

项目要求:1.对用户的煤.水.电的使用金额对用户进行每周短信提醒. 2.当爬虫爬来的煤.水.电的剩余金额小于10元时,对用户进行短信提醒. 数据库描诉:mongodb  建了4张表    分别分 每周提醒表.水费表.电费表.煤气表 每周提醒表:用户名.用户id.是否开/关提醒功能.用户是否关联煤.水.电.用户电话 电费表:用户名.用户id.社区编号.供电公司编号.是否开/关提醒.剩余电费.用户电话.更新时间.上次更新时间.上次更新剩余电费. 遇到问题描诉:使用nodejs开发,实现每周六晚上短信

从一个nodejs爬虫来看nodejs异步回调嵌套时数据的安全性[原创,未经授权禁止转载]

代码如下: /** * Created by Totooria Hyperion on 2016/4/20. */ 'use strict'; const fs = require('fs');const path = require('path');const cheerio = require('cheerio');const request = require('request'); function getCategoryByUrl(url,callback){ request(url,

Nodejs - 框架类库 - Nodejs异步流程控制Async

简介 Async是一个流程控制工具包,提供了直接而强大的异步功能 应用场景 业务流程逻辑复杂,适应异步编程,减少回调的嵌套 安装 npm insatll async 函数介绍 Collections each: 如果想对同一个集合中的所有元素都执行同一个异步操作. 1 var async = require('async'); 2 3 var t = require('./t'); 4 var log = t.log; 5 6 /** 7 * 8 * async提供了三种方式: 9 * 1. 集