javascript异步编程解决方案

*本文大部分内容来自《深入浅出node.js》
众所周知,node与其他后台不同的很大一部分原因在于其异步I/O,异步使得程序运行不会被阻塞,这也是node的性能所在。但是异步编程往往不是那么友好,在此我们为异步编程归纳了一些常用的解决方案,这些方案同样适合在客户端采纳。
1.事件发布/订阅机制
2.Promise/Deferred模式
3.流程控制库
 

事件发布/订阅模式

事件发布/订阅模式使用很简单,如下所示:

emitter.on("event1",function(message){
    alert(message);
});
emitter.emit("event1","Hello Baby");

订阅事件实际上就是一个高阶函数的应用,将回调函数传入一个第三方,然后在合适的时机取出来运行就可以了。可以理解为将原来两者的耦合关系解开,插入一个第三者,原来的两者都同时与这插进来的第三个者耦合。基础实现的代码比较简单,就不写出来了。

*如果对同一个事件添加了超过10个侦听,在node中会得到一条警告,设计者认为可能造成内存泄漏,可以使用emitter.setMaxListeners(0)来将这个限制去掉。

对于事件发布/订阅模式,一个推荐模块是EventProxy模块,该模块提供了all(),tail(),after(),fail(),done()等方法,使用起来十分便捷,详细不再展开。


Promise/Deferred模式

事件发布/订阅模式需要预先定义后续的执行流程,而Promise/Deferred模式则是先执行异步,延迟传递处理的方式。

Promise/A模型中规定了一个Promise对象只要具备then()方法即可,then()方法中接收成功与失败的回调。Promise/Deferred模式实际上也是使用了事件发布/订阅模式来完成的,简单实现如下:

var Promise = function(){
    EventEmitter.call(this);
};
util.inherits(Promise,EventEmitter);

Promise.prototype.then = function(successHandler,errorHandler,progressHandler){
    if(typeof successHandler === "function"){
        this.once("success",successHandler);
    }
    if(typeof errorHandler === "function"){
        this.once("error",errorHandler);
    }
    if(typeof progressHandler === "function"){
        this.once("progress",progressHandler);
    }
    return this;
};

var Deferred = function(){
    this.state = "unSuccess";
    this.promise = new Promise();
};

Deferred.prototype.resolve = function(obj){
    this.state = "success";
    this.promise.emit("success",obj);
};
Deferred.prototype.reject = function(obj){
    this.state = "error";
    this.promise.emit("error",obj);
};
Deferred.prototype.progress = function(obj){
    this.promise.emit("progress",obj);
};

定义一个Promise对象继承node的events模块,在promise原型上定义了then()方法,接收三个参数,分别是成功回调,失败回调和progress回调。在then()方法中利用once注册了三个事件,然后返回this以便链式调用(关于链式调用详细实现建议阅读《深入浅出node.js》异步编程部分)。

定义了一个Deferred对象,在里面定义了状态,同时将Promise对象拿过来以便能用到events提供的api。定义了resolve,reject和progress方法响应在Promise中注册的事件。至于Promise/Deferred模式的使用还是十分简洁明了,就不再赘述了。


流程控制库

最知名的流程控制模块非async莫属,在node开发中,流程控制是最基本的需求。async模块提供了一系列的api来满足这些需求,其内部实现主要是对回调函数的注入。

series()方法实现一组任务的串行执行;parallel()方法实现一组任务并行执行;waterfall方法实现了有依赖的异步串行执行;甚至提供了强大的auto方法根据依赖关系自动分析,以最佳的顺序执行。

简单解释一下其中series()的实现:

async.series([
    function(cb){
        fs.readFile('file1','utf-8',cb);
    },
    function(cb){
        fs.readFile('file2','utf-8',cb);
    }
],function(error,results){
    //results => [file1,file2]
});

等同于:

fs.readFile('file1','utf-8',function(err,content){
    if(err){
        return cb(err);
    }
    fs.readFile('file1','utf-8',function(err, data){
            if(err){
                return cb(err);
            }
            cb(null,[content,data]);
        }
    );
});


下面那段代码再多来几个异步就成了“回调地狱”,而async模块给我们封装好了这段代码,我们只需要愉快的写成优雅的顺序模式就好了。

*在node的异步并发中有一个问题就是当并发量过大时,下层服务器会吃不消,因为node的I/O操作是调用子线程去实现的,并且并发量过大,操作系统的文件描述符数量会用光并抛出错误too many open files。在这里一个解决方案就是使用bagpipe模块,该模块可以对异步API进行过载保护。


ok,异步编程解决方案就介绍到这里,有兴趣的同学还可以去看看ES7的Async/Await模块,那也是一种很优雅的异步编程方式。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值