目前,异步编程的主要解决方案有三种:
事件发布/订阅模式
Promise/Deferred模式
流程控制库
一、事件发布/订阅模式
事件监听器模式
是广泛用于异步编程的模式,是回调函数的模式化,又称事件发布/订阅模式
Node自身提供的events模块
是发布/订阅模式的简单实现,它具有addListener/on()
、once()
、emit
、addListener
等基本的事件监听模式的方法实现。
实例代码:
const events = require("events");
const emitter = new events.EventEmitter();
// 订阅
emitter.on("eventName",function(err,data){
console.log(data);
})
// 发布
emitter.emit("eventName",err,data);
订阅事件就是一个高阶函数的应用。事件发布/订阅模式可以实现一个事件与多个回调函数的关联,这些回调函数又称为事件侦听器。
- 通过emit发布事件后,消息立即传递给当前事件的所有侦听器执行
- 事件发布者无须关注订阅的侦听器如何实现业务逻辑,以及侦听器的数量,数据可以灵活传递
- 事件侦听器也是一种钩子机制,利用钩子导出内部数据或状态给外部的调用者,获取对象在运行期间的中间值或内部状态
-
如果对一个事件添加超过了10个侦听器,会得到警告;可能导致内存泄露。设置
emitter.setMaxListeners(0)
可以去掉此限制 -
为了处理异常,EventEmitter对象对error事件进行了特殊对待。如果在运行期间的错误触发了error事件,EventEmitter会检查是否有对error事件添加侦听器,如果添加了,错误交给侦听器处理;如果外部没有捕获这个异常,则会引起线程退出
- 继承events模块
通过util的模块可以轻松继承EventEmitter
const events = require("events");
const util = require("util");
function Events(){
return events.EventEmitter.call(this);
}
util.inherits(Events,events.EventEmitter);
- 利用事件队列解决雪崩问题
所谓雪崩问题就是在
高访问量,大并发量的情况下缓存失效
的情景,此时大量的请求同时涌入数据库
中,数据库无法同时承受
如此大的查询请求,进而影响到网站整体的响应速度。
// 引入事件队列
var proxy = new events.EventEmitter()
proxy.once('selected',callback)
利用once
方法,所有请求的回调都压入事件队列中,执行一次就会移除监听器,保证每个回调只会执行一次
二、Promise/Deferred模式
- 在原始的API中,一个事件只能处理一个回调,而通过Deferred对象,可以对事件加入任意的业务处理逻辑。
- Promise/Deferred模式包含两部分:即Promise和Deferred。
在这里插入代码片
Promise/A
- promise操作三种状态:未完成pending、完成resolve、失败reject
- then()方法只接受function对象;并继续返回Promise对象,实现链式调用
Promise.prototype.then = function(){
....}
Deferred
Deferred.prototype.resolve =function(obj){
this.state ='fulfilled'
this.promise.emit('success',obj)
}
Deferred.prototype.reject=function(err){
this.state ='failed'
this.promise.emit('error')
}
Deferred.prototype.progress=function(data){
this.promise.emit('progress',data)
}
Deferred主要用于内部,维护异步模型的状态,将业务中的不可变部分封装在此
Promise作用于外部,通过then方法暴露给外部以添加自定义逻辑,将可变部分交给Promise
三、流程控制库
1、async流程控制模块
- series:一组任务的串行执行
async.series([
function(callback){
fs.readFile("./profile.jpg","utf-8",callback);
},
function(callback){
fs.readFile("./profile.jpg","utf-8",callback);
}
],function(err,results){
console.log(results)
})
- parallel 并行
async.parallel([
function(callback){
fs.readFile("./profile.jpg","utf-8",callback);
},
function(callback){
fs.readFile("./profile.jpg","utf-8",callback);
}
],function(err,results){
console.log(results)
})
- waterfall 异步调用的依赖处理
2、Step
step只有一个接口,通过npm install
安装即可