• Posts tagged "async"

Blog Archives

Nodejs异步异常处理domain

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-core-domain/ ‎

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/‎

打赏作者

Async多任务时间管理

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-async-timer/

nodejs-async-timer

前言

做服务器端开发时,经常会遇到时间管理的功能需求,比如每2秒刷新一次,每三分钟做一次统计计算,周一至周五9点30启动一个定时任务等等。

很多时候我们会把这些定时任务,交给linux系统的Crontab来实现。不过,有时为了增加系统的灵活性,我们需要在服务器后台实现。

对于单线程的Nodejs,如何控制多任务的时间管理呢?

目录

  1. 需求描述
  2. Nodejs的实现方案setInterval
  3. Async多任务时间管理

1. 需求描述

基于Nodejs的express3构建的web框架,需要在周一至周五,早上9点15分时,分别启动程序A和程序B,程序C。下午16点程序A,B,C停止。

程序A: 每1秒去redis取数据一次,保留在Nodejs的全局变量G中。
程序B: 每10秒去mysql取数据一次,通过websocket直接访问给客户端。
程序C: 每5秒对全局变量G,进行平均值计算,然后通过websocket直接访问给客户端。

2. Nodejs的实现方案setInterval

系统环境:

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

出初化项目:


~ cd D:\workspace\javascript\nodejs-async\demo
~ express -e timers
~ cd timers && npm install
~ npm install moment
~ npm install twix

编辑文件:app.js,在文件最面下增加新代码


...

//moment,twix时间工具
var  moment = require('moment')
    ,twix = require('twix');

//判断程序启动时间
function isTime(){
    var hms = 'HHmmss';
    return moment("091500",hms).twix(moment("160000",hms)).contains(moment());
}

//打印日志
if(isTime()){
    console.log("===============Working time===================");
}

//日志时间格式化
function now() {
    return moment().format("HH:mm:ss");
}

//全局变量G
var G = 0;

//模拟程序A
function A() {
    console.log(now() + " A(s1)=> {G:" + (G++) + "} Cache G");
}

//模拟程序B
function B() {
    console.log(now() + " B(s10)=> {B:10} TO client");
}

//模拟程序C
function C() {
    console.log(now() + " C(s5)=> {G:" + (G / 5) + "} TO client");
    G = 0;
}

//分别对A,B,C程序进行时间管理
setInterval(function () {
    if(isTime()){
        A()
    };
}, 1000);

setInterval(function () {
    if(isTime()){
        C();
    }
}, 5 * 1000);

setInterval(function () {
    if(isTime()) {
        B();
    }
}, 10 * 1000);

运行nodejs,查看日志输出


D:\workspace\javascript\nodejs-async\demo\timers>node app.js
===============Working time===================
Express server listening on port 3000
15:02:24 A(s1)=> {G:0} Cache G
15:02:25 A(s1)=> {G:1} Cache G
15:02:26 A(s1)=> {G:2} Cache G
15:02:27 A(s1)=> {G:3} Cache G
15:02:28 A(s1)=> {G:4} Cache G
15:02:28 C(s5)=> {G:1} TO client
15:02:29 A(s1)=> {G:0} Cache G
15:02:30 A(s1)=> {G:1} Cache G
15:02:31 A(s1)=> {G:2} Cache G
15:02:32 A(s1)=> {G:3} Cache G
15:02:33 A(s1)=> {G:4} Cache G
15:02:33 C(s5)=> {G:1} TO client
15:02:33 B(s10)=> {B:10} TO client
15:02:34 A(s1)=> {G:0} Cache G
15:02:35 A(s1)=> {G:1} Cache G
15:02:36 A(s1)=> {G:2} Cache G
15:02:37 A(s1)=> {G:3} Cache G
15:02:38 A(s1)=> {G:4} Cache G
15:02:38 C(s5)=> {G:1} TO client
15:02:39 A(s1)=> {G:0} Cache G
15:02:40 A(s1)=> {G:1} Cache G
15:02:41 A(s1)=> {G:2} Cache G
15:02:42 A(s1)=> {G:3} Cache G
  • 程序A,每1秒运行一次,给G+1。
  • 程序B,每10秒运行一次,输出到客户端。
  • 程序C,每5秒运行一次,取G的平均值,给G赋值为0,输出到客户端。

虽然完成了功能需求,但是代码不美观!如果再增加任务D,E,F….代码不好维护。

3. Async多任务时间管理

下面我们用async包,对上面的浏览进行封装。

Async包函数的详细介绍,请参考文章:Nodejs异步流程控制Async

安装async依赖


~ npm install async

新建一个文件:async-app.js,


~ cp app.js async-app.js

也就是重新复制一份app.js, 把setInterval的函数都去掉。

增加async的时间管理代码


var async = require('async');

var arr = [
    {fun: A, delay: 1000, test: isTime},
    {fun: B, delay: 10 * 1000, test: isTime},
    {fun: C, delay: 5 * 1000, test: isTime}
];

async.each(arr, function (item, callback) {
    async.whilst(item.test,function(cb) {
            item.fun();
            setTimeout(cb, item.delay);
        },function(err) {
            console.log("Not working time!");
        }
    );
}, function (err) {
    log('Error: ' + err);
});

运行nodejs,查看日志输出


D:\workspace\javascript\nodejs-async\demo\timers>node async-app.js
===============Working time===================
15:13:57 A(s1)=> {G:0} Cache G
15:13:57 B(s10)=> {B:10} TO client
15:13:57 C(s5)=> {G:0.2} TO client
Express server listening on port 3000
15:13:58 A(s1)=> {G:0} Cache G
15:13:59 A(s1)=> {G:1} Cache G
15:14:00 A(s1)=> {G:2} Cache G
15:14:01 A(s1)=> {G:3} Cache G
15:14:02 C(s5)=> {G:0.8} TO client
15:14:02 A(s1)=> {G:0} Cache G
15:14:03 A(s1)=> {G:1} Cache G
15:14:04 A(s1)=> {G:2} Cache G
15:14:05 A(s1)=> {G:3} Cache G
15:14:06 A(s1)=> {G:4} Cache G
15:14:07 B(s10)=> {B:10} TO client
15:14:07 C(s5)=> {G:1} TO client
15:14:07 A(s1)=> {G:0} Cache G
15:14:08 A(s1)=> {G:1} Cache G
15:14:09 A(s1)=> {G:2} Cache G
15:14:10 A(s1)=> {G:3} Cache G
15:14:11 A(s1)=> {G:4} Cache G
15:14:12 C(s5)=> {G:1} TO client
15:14:12 A(s1)=> {G:0} Cache G
  • 构建一个arr数组,封装调用的A,B,C的参数。
  • 使用async.each函数,对arr的item异步并行
  • 使用async.whilst函数,对任务启动时间进行判断,并根据delay运行任务

程序运行结果与setInterval一样。而代码更利于维护,一旦需要增减任务,简单地修改arr的数组就行了,其他的代码都不用动!!

很难想象,其他的语言能像js一样,有如此之重构的能力。越来越感到惊喜!

转载请注明出处:
http://blog.fens.me/nodejs-async-timer/

打赏作者

Nodejs异步流程控制Async

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-async/

nodejs-async

前言

Nodejs框架类库很多,功能相近的框架,本来只打算学一种写一种。之前写过流程控制框架windjs文章,本来是想着要支持一下“国人框架”。无奈啊,作者竟然放弃了维护,国人真的不靠谱啊!

“流程控制”本来是件比较简单的事,但是由于Nodejs的异步架构的实现方法,对于需要同步的业务逻辑,实现起来就比较麻烦。嵌套3-4层,代码就会变得的支离破碎了!

今天就遇到了一个业务逻辑,连续对数据库操作,前后有依赖。让我们看看Async是如何解决问题的。

不用不知道,一用真强大!!

目录

  1. Async介绍
  2. Async安装
  3. Async函数介绍
  4. async_demo使用介绍
  5. 场景:对数据库的连续操作
  6. async_demo我的分支

1. Async介绍

Async是一个流程控制工具包,提供了直接而强大的异步功能。基于Javascript为Node.js设计,同时也可以直接在浏览器中使用。

Async提供了大约20个函数,包括常用的 map, reduce, filter, forEach 等,异步流程控制模式包括,串行(series),并行(parallel),瀑布(waterfall)等。

项目地址:https://github.com/caolan/async

2. Async安装

我的系统环境

  • win7 64bit
  • Nodejs:v0.10.5
  • Npm:1.2.19
  • MySQL:Server version: 5.6.11 MySQL Community Server (GPL)

我们做实验时,安装async有两个方式:

  • 1. 独立安装async
  • 2. 下载async demo代码安装

我建议大家用第二种方式安装,这样子实例的代码就都有了。

1). 独立安装async


~ D:\workspace\javascript>mkdir nodejs-async && cd nodejs-async
~ D:\workspace\javascript\nodejs-async>npm install async
npm http GET https://registry.npmjs.org/async
npm http 304 https://registry.npmjs.org/async
async@0.2.9 node_modules\async

打开网页,参照示例学习:https://github.com/bsspirit/async_demo

2). 下载async demo代码安装


~ D:\workspace\javascript>git clone git@github.com:bsspirit/async_demo.git nodejs-async
~ D:\workspace\javascript>cd nodejs-async
~ D:\workspace\javascript\nodejs-async>npm install
npm http GET https://registry.npmjs.org/moment
npm http GET https://registry.npmjs.org/async
npm http 304 https://registry.npmjs.org/moment
npm http 304 https://registry.npmjs.org/async
async@0.2.9 node_modules\async
moment@2.1.0 node_modules\moment

这套demo示例,比较全面的介绍了async的使用,有中文注释。 感谢github社区原创作者freewind,代码更新的贡献者alsotang。

当然,我的分支中也修改了一部分代码。在本文最后,我会写到changelog中!

3. Async函数介绍

基于async的0.2.9版本。

async主要实现了三个部分的流程控制功能:

  • 集合: Collections
  • 流程控制: Control Flow
  • 工具类: Utils

1). 集合: Collections

  • each: 如果想对同一个集合中的所有元素都执行同一个异步操作。
  • map: 对集合中的每一个元素,执行某个异步操作,得到结果。所有的结果将汇总到最终的callback里。与each的区别是,each只关心操作不管最后的值,而map关心的最后产生的值。
  • filter: 使用异步操作对集合中的元素进行筛选, 需要注意的是,iterator的callback只有一个参数,只能接收true或false。
  • reject: reject跟filter正好相反,当测试为true时则抛弃
  • reduce: 可以让我们给定一个初始值,用它与集合中的每一个元素做运算,最后得到一个值。reduce从左向右来遍历元素,如果想从右向左,可使用reduceRight。
  • detect: 用于取得集合中满足条件的第一个元素。
  • sortBy: 对集合内的元素进行排序,依据每个元素进行某异步操作后产生的值,从小到大排序。
  • some: 当集合中是否有至少一个元素满足条件时,最终callback得到的值为true,否则为false.
  • every: 如果集合里每一个元素都满足条件,则传给最终回调的result为true,否则为false
  • concat: 将多个异步操作的结果合并为一个数组。

2). 流程控制: Control Flow

  • series: 串行执行,一个函数数组中的每个函数,每一个函数执行完成之后才能执行下一个函数。
  • parallel: 并行执行多个函数,每个函数都是立即执行,不需要等待其它函数先执行。传给最终callback的数组中的数据按照tasks中声明的顺序,而不是执行完成的顺序。
  • whilst: 相当于while,但其中的异步调用将在完成后才会进行下一次循环。
  • doWhilst: 相当于do…while, doWhilst交换了fn,test的参数位置,先执行一次循环,再做test判断。
  • until: until与whilst正好相反,当test为false时循环,与true时跳出。其它特性一致。
  • doUntil: doUntil与doWhilst正好相反,当test为false时循环,与true时跳出。其它特性一致。
  • forever: 无论条件循环执行,如果不出错,callback永远不被执行。
  • waterfall: 按顺序依次执行一组函数。每个函数产生的值,都将传给下一个。
  • compose: 创建一个包括一组异步函数的函数集合,每个函数会消费上一次函数的返回值。把f(),g(),h()异步函数,组合成f(g(h()))的形式,通过callback得到返回值。
  • applyEach: 实现给一数组中每个函数传相同参数,通过callback返回。如果只传第一个参数,将返回一个函数对象,我可以传参调用。
  • queue: 是一个串行的消息队列,通过限制了worker数量,不再一次性全部执行。当worker数量不够用时,新加入的任务将会排队等候,直到有新的worker可用。
  • cargo: 一个串行的消息队列,类似于queue,通过限制了worker数量,不再一次性全部执行。不同之处在于,cargo每次会加载满额的任务做为任务单元,只有任务单元中全部执行完成后,才会加载新的任务单元。
  • auto: 用来处理有依赖关系的多个任务的执行。
  • iterator: 将一组函数包装成为一个iterator,初次调用此iterator时,会执行定义中的第一个函数并返回第二个函数以供调用。
  • apply: 可以让我们给一个函数预绑定多个参数并生成一个可直接调用的新函数,简化代码。
  • nextTick: 与nodejs的nextTick一样,再最后调用函数。
  • times: 异步运行,times可以指定调用几次,并把结果合并到数组中返回
  • timesSeries: 与time类似,唯一不同的是同步执行

3). 工具类: Utils

  • memoize: 让某一个函数在内存中缓存它的计算结果。对于相同的参数,只计算一次,下次就直接拿到之前算好的结果。
  • unmemoize: 让已经被缓存的函数,返回不缓存的函数引用。
  • log: 执行某异步函数,并记录它的返回值,日志输出。
  • dir: 与log类似,不同之处在于,会调用浏览器的console.dir()函数,显示为DOM视图。
  • noConflict: 如果之前已经在全局域中定义了async变量,当导入本async.js时,会先把之前的async变量保存起来,然后覆盖它。仅仅用于浏览器端,在nodejs中没用,这里无法演示。

4. async_demo使用介绍

详细使用请参考github源代码:https://github.com/bsspirit/async_demo

每个函数的用法,有非常详细的实例!!

5. 场景:对数据库的连续操作

这个场景进背景情况,请参考文章:用Nodejs连接MySQL

原场景中,对数据串行操作,增删改查(CRUD),代码如下:


var mysql = require('mysql');
var conn = mysql.createConnection({
    host: 'localhost',
    user: 'nodejs',
    password: 'nodejs',
    database: 'nodejs',
    port: 3306
});
conn.connect();

var insertSQL = 'insert into t_user(name) values("conan"),("fens.me")';
var selectSQL = 'select * from t_user limit 10';
var deleteSQL = 'delete from t_user';
var updateSQL = 'update t_user set name="conan update"  where name="conan"';

//delete
conn.query(deleteSQL, function (err0, res0) {
    if (err0) console.log(err0);
    console.log("DELETE Return ==> ");
    console.log(res0);

    //insert
    conn.query(insertSQL, function (err1, res1) {
        if (err1) console.log(err1);
        console.log("INSERT Return ==> ");
        console.log(res1);

        //query
        conn.query(selectSQL, function (err2, rows) {
            if (err2) console.log(err2);

            console.log("SELECT ==> ");
            for (var i in rows) {
                console.log(rows[i]);
            }

            //update
            conn.query(updateSQL, function (err3, res3) {
                if (err3) console.log(err3);
                console.log("UPDATE Return ==> ");
                console.log(res3);

                //query
                conn.query(selectSQL, function (err4, rows2) {
                    if (err4) console.log(err4);

                    console.log("SELECT ==> ");
                    for (var i in rows2) {
                        console.log(rows2[i]);
                    }
                });
            });
        });
    });
});

//conn.end();

为了实现了串行操作,所有的调用都是在callback中实现的,5层嵌套结构。这种代码已经变得不可以维护了。所以,需要用async库,对上面的代码结构进行重写!

修改后的代码


var mysql = require('mysql');
var async = require('async');

var conn = mysql.createConnection({
    host: 'localhost',
    user: 'nodejs',
    password: 'nodejs',
    database: 'nodejs',
    port: 3306
});

var sqls = {
    'insertSQL': 'insert into t_user(name) values("conan"),("fens.me")',
    'selectSQL': 'select * from t_user limit 10',
    'deleteSQL': 'delete from t_user',
    'updateSQL': 'update t_user set name="conan update"  where name="conan"'
};

var tasks = ['deleteSQL', 'insertSQL', 'selectSQL', 'updateSQL', 'selectSQL'];
async.eachSeries(tasks, function (item, callback) {
    console.log(item + " ==> " + sqls[item]);
    conn.query(sqls[item], function (err, res) {
        console.log(res);
        callback(err, res);
    });
}, function (err) {
    console.log("err: " + err);
});

控制台输出


deleteSQL ==> delete from t_user
{ fieldCount: 0,
  affectedRows: 0,
  insertId: 0,
  serverStatus: 34,
  warningCount: 0,
  message: '',
  protocol41: true,
  changedRows: 0 }
insertSQL ==> insert into t_user(name) values("conan"),("fens.me")
{ fieldCount: 0,
  affectedRows: 2,
  insertId: 45,
  serverStatus: 2,
  warningCount: 0,
  message: '&Records: 2  Duplicates: 0  Warnings: 0',
  protocol41: true,
  changedRows: 0 }
selectSQL ==> select * from t_user limit 10
[ { id: 45,
    name: 'conan',
    create_date: Fri Sep 13 2013 12:24:51 GMT+0800 (中国标准时间) },
  { id: 46,
    name: 'fens.me',
    create_date: Fri Sep 13 2013 12:24:51 GMT+0800 (中国标准时间) } ]
updateSQL ==> update t_user set name="conan update"  where name="conan"
{ fieldCount: 0,
  affectedRows: 1,
  insertId: 0,
  serverStatus: 2,
  warningCount: 0,
  message: '(Rows matched: 1  Changed: 1  Warnings: 0',
  protocol41: true,
  changedRows: 1 }
selectSQL ==> select * from t_user limit 10
[ { id: 45,
    name: 'conan update',
    create_date: Fri Sep 13 2013 12:24:51 GMT+0800 (中国标准时间) },
  { id: 46,
    name: 'fens.me',
    create_date: Fri Sep 13 2013 12:24:51 GMT+0800 (中国标准时间) } ]
err: null

代码一下读性就增强了许多倍,这就是高效的开发。

不用不知道,一用真强大!!!

当然还有其他的工作流框架来完成这件事情step,then.js,windjs。
windjs请参考:wind.js助力异步编程

6. async_demo我的分支

https://github.com/bsspirit/async_demo

  • 1. forEach.js改名为each.js
  • 2. each.js文件中的async.forEach,改为async.each
  • 3. map.js增加mapLimit函数
  • 4. filter_reject.js对注释补充
  • 5. filter_reject.js增加rejectSeries函数
  • 6. reduce.js增加注释
  • 7. detect.js增加注释
  • 8. sortBy.js增加注释
  • 9. some.js对注释补充
  • 10. every.js对注释补充和修改
  • 11. series.js调整注释格式
  • 12. parallel.js调整注释格式
  • 13. parallel.js增加parallelLimit函数
  • 14. whilist_until.js调整注释格式
  • 15. whilist_until.js增加doWhilst函数
  • 16. whilist_until.js增加doUntil函数
  • 17. whilist_until.js增加forever函数
  • 18. waterfall.js调整注释格式
  • 19. 增加compose.js文件
  • 20. apply.js补充注释并增加一个实例
  • 21. 修改nextTick.js实例
  • 22. 增加times.js文件,包括times和timesSeries函数
  • 23. 修改iterator.js实例
  • 24. 修正auto.js关于第二个实例的错误解释
  • 25. 修改queue.js实例和注释
  • 26. 修改cargo.js文件
  • 27. 增加applyEach.js文件
  • 28. 修改utils.js实例和注释

转载请注明出处:
http://blog.fens.me/nodejs-async/

打赏作者