一、事件发布/订阅模式(events)
事件发布/订阅模式自身并无同步和异步的问题,但在node中,emit()调用多半是伴随时间循环而异步触发的。
订阅:
emitter.on("event1", function(message){
console.log(message);
});
发布
emitter.emit("event1","I am message")
注意事项:
- 如果对一个事件添加了超过10个侦听器,将会得到一条警告。因为如果事件相关的监听器过多,可能会导致CPU占用过多。
- 一个健壮的EventEmitter实例应该对error对象做处理
**雪崩问题:**在高访问量、大并发量的情况下缓存失效的场景,此时大量的请求同时涌入数据库中,数据库无法同时承受如此大的查询请求,进而影响到网站整体的响应速度。
**解决方法:**引入事件队列
var proxy = new events.EventEmitter();
var status = "ready";
var select = function(callback){
proxy.once("selected", callback);
if (status === "ready"){
status = "spending";
db.select("SQL", function(results){
status = "ready";
callback(results);
})
}
}
这里我们利用了once()方法,将所有请求的回调都压入事件队列中,利用其执行一次就会将监听器移除的特点,保证每一次回调只会被执行一次。这样就保证了,对于相同的SQL语句,同一个查询开始到结束的过程永远只有一次。一旦查询结束,得到的结果可以被这些调用者共同使用。
另外,此处可能因为存在监听器躲过而引发警告,需要调用下边的代码移除警告
setMaxListeners(0)
**多异步之间的协作方案:**利用偏函数来处理哨兵变量和第三方函数的关系
var emitter = new events.Emitter();
var done = after(times, render);
// 多对多
emitter.on("done", done);
emitter.on("done", other);
var after = function(times, callback){
var count = 0, results = {};
return function (key, value) {
results[key] = value;
count ++;
if (count === times){
callback(results);
}
}
}
db.query(sql, function(err, data){
emitter.emit("done", "data", data)
});
fs.readFile(template_path, "utf-8", function(err, template){
emitter.emit("done", "template", template);
})
- all()方法:当all()方法中订阅的多个事件都被触发后,才会执行侦听器。
var ep = new EventProxy();
ep.all("template", "data", function(template, data){
// TODD
});
fs.readFile(template_path, "utf-8", function(err, template){
ep.emit("template", temp