让MySQL搜索区分大小写或排序时分大小写方法如下: 1.在SQL中强制 SELECT `field` FROM `table` WHERE BINARY…
Node.js: 如何继承 events 自定义事件及触发函数
events 是node.js的核心api ,几乎大部分node.js 的api都继承 events 类(javascript中没有类,也不存在继承,确切说是模拟类和继承,点击查看)
比如我们常见的 http , stream ,fs …..等等.
因为node.js的实现就是异步非阻塞io,通过事件轮询触发异步回调的机制,在单线程运行模式下,合理应用服务器资源才是制胜法宝,正是因为事件轮询才使得异步IO在高并发处理下游刃有余,所以大部分的包去继承 events 似乎合情合理.
下面我们可以看一个常见的事件处理,但是我们用的时候似乎并没有当回事:
var req = http.request(params, function(res) { res.on("data", function(data) { console.log("response data", data); }); res.on("end", function() { console.log("response end"); }); }); req.end();
上面是一个标准的异步模式,而作为单线程的node.js绝对不会选择等待,那这个直接在node.js是如何处理的.
node.js运行在单线程模式下,但是可以有多个进程来处理,我们假设A进程执行api操作,B进程负责事件轮询.那么A调用上面的代码会马上得到一个结果,结果内容就是:”你的请求已经受理了” 然后A进程接着处理后面排队的其他api.
这里来一个小插曲:
” 如果是其他同步语言,会这样处理,执行到上面的代码以后会得得到什么结果? 什么也没有,只能一直在这里站着等待结果….后面的api也全部排队等待中ing…..突然A大声喊道”有结果了”,这一声差点把睡着的api吓 尿,然后它拿起结果屁颠屁颠跑了,接着下一个api来请求…..
所以同步语言该如何解决这种资源浪费? 多开几个窗口,大家分散在不同窗口排队(这就是多线程) 然后各个线程要互相通报状态保持数据同步等….而node.js就避免了这种顾虑”
我们从插曲回来
上面的api调用后 A马上告知它结果,但是这个结果并不是它想要的.
于是B进程该上场表现了,它手里拿着一大串事件处理函数的引用.
当它发现了 “data” 事件,马上把 “data” 事件对应的处理函数引用给了”data”事件,
同理当它遇到 “end” 事件,马上把 “end”事件处理函数塞给了”end” ,
当某个事件执行完返回数据时马上交给B,而B再大声通知刚才调用的api,”你的业务逻辑数据返回来了”,
这个可能刚才那个请求api已经吃了个盖饭,正在悠然的抽着烟,然后很娴熟的掐掉烟头,跑回去拿到了自己业务逻辑所需要的数据.
从上面描述可以看到,api在调用过程中一共拿到了2个结果:
第一个就是把代码提交进程A的时候,A马上给它返回一个结果”你的请求已经受理”,虽然这个结果对业务没啥作用,但是对于api是有用的,它得知这个通知后,就不需要等待了.自己出去吃饭抽烟该干啥干啥去.
第二个得到的结果才是真正数据处理完以后的结果,这部分数据可能业务逻辑非常需要.
其中拿到第二个结果的过程中,B进程手提事件绑定函数,四处奔波寻找事件.
这是一个什么过程,就是当事件被触发时,就马上执行事件绑定函数.所以前提是:首先有事件绑定函数,其次注册了事件,(所以上面会提到B进程手里拿着事件绑定函数坐等事件)
看来要使用事件,首先我们需要有事件绑定函数,其次需要注册事件,而在node.js中 events 核心库正好实现了这几个api
events.EventEmitter 类提供了如下api
1.事件绑定函数
emitter.on(event, function(){ //业务处理 });
还有另外一种绑定函数的方法,很不常用,和上面的等价
emitter.addListener(event,function(){ //业务处理 });
2.绑定一次性函数,和上面的一样,给某个事件绑定一个函数,不同的是,对此事件只监听一次,也就是说,这个绑定函数只运行一次.
emitter.once(event,function(){ //业务处理 });
3.移除一个事件绑定函数
emitter.removeListener(event,function(){ //移除指定事件 });
4.移除所有绑定事件函数,注意参数是数组,数组元素是事件名称
emitter.removeAllListener([event,...]);
5.设置事件绑定函数上限,
node.js 建议我们在某个事件上的绑定函数不要超过10个,如果达到这个上限会予以警告,如何消除这个警告?用下面的api
emitter.setMaxListeners(n); //这样一个事件最多可以绑定n个函数
6.事件发射器,注册事件
emitter.emit(event,[arg1],[arg2])
用这个api注册事件,也就是自定义事件.
剩下的 几个api不一一列举了.
如何在一个自定义的类里使用事件? 答案是: 继承!
让你定义的类继承 events ,然后就可以使用上面的api了.
来一个实际的示例.一个SNS社区,有博客,问答,小组等模块,每个模块都有用户数据入口,比如 用户写博客要保存, 提问一个问题也要保存,在小组发表一个话题也要保存.
期间,不论我保存博客,问题还是小组话题,我都需要把数据写入数据库,统计个板块内容数量,统计用户发内容的数量.
如果我创建博客类,问答类,小组话题类,然后每个类再实现上面功能,这是一个重复且耗时的过程.这个应用场景就非常适合事件,重复且调用频繁
eventTest 是这个示例的项目文件夹
methord 为逻辑应用层(或者为方法层) //处理业务逻辑 ,调用 模型层实体对象的方法
model 为模型层(里面有一个父类 _base.js 和若干个子类,暂时只有一个blogInfo.js 博客类) //个性化定义模型 属性,方法,事件
app.js 启动文件 //启动服务器,解析路由,调用方法层api (不做任何业务逻辑处理, 调用 –> 输出给用户 )
愿景:
有没有一种可能,我创建一个公用基础类_base,此类有一个工厂方法onEvent, onEvent专门用来绑定事件函数.
还有另外一个工厂方法emitEvent, emitEvent 专门用来注册发射事件.
代码实现:
var events=require('events'); var util=require('util'); function _base(){ this.emitter=new events.EventEmitter(this); }; util.inherits(_base,events.EventEmitter); //继承 _base.prototype.onEvent=function(eventName,callback){ this.emitter.on(eventName,callback); } _base.prototype.emitEvent=function(eventName,arg){ this.emitter.emit(eventName,arg); } module.exports=_base;
注意: node.js 中实现继承要使用工具类 util 中的 inherits 方法,下一篇博文专门用来讲此继承方式
model 实体层:
博客类 blogInfo –> 继承自上面 _base 基础类
小组类 groupInfo –> 继承自上面 _base 基础类
问答类 askInfo –> 继承自上面 _base 基础类
我们只用 blogInfo 来举例说明,其他2个类大同小异:
因为blogInfo 继承了 _base 类,所以它也有了2个工厂方法,因为针对不同的类,可能需要发射不同的事件,定义不同的事件监听函数.
我们想实现在博客保存的时候, 内容提交到数据库,统计板块数量,统计用户内容数量,保存结束.
所以我们把个性化事件及事件绑定函数放在此类里定义:
var _base=require('./_base'); var util=require('util'); function blogInfo(){ this.base=new _base(); } util.inherits(blogInfo,_base); blogInfo.prototype.onSave=function(blog){ this.base.onEvent('saveStart',function(blog){ console.log('saveStart',blog); }); this.base.onEvent('blogCount',function(blog){ console.log('blogCount',blog); }); this.base.onEvent('userInfoCount',function(blog){ console.log('count',blog); }); this.base.onEvent('saveEnd',function(blog){ console.log('saveEnd',blog); }); }; blogInfo.prototype.emitEvent=function(blog){ this.base.emitEvent('saveStart',blog); this.base.emitEvent('blogCount',blog); this.base.emitEvent('userInfoCount',blog); this.base.emitEvent('saveEnd',blog); };
上面的blogInfo 继承了 _base ,然后在原型对象上定义方法onSave , onSave 方法里调用父类的 onEvent 工厂方法分别给4个事件, saveStart ,blogCount, userInfoCount , saveEnd 定义了对应事件的绑定函数.
接着blogInfo 在原型对象上定义了方法 emitEvent 方法,并在方法里发射了4个事件 saveStart ,blogCount, userInfoCount , saveEnd
其他模型类似blogInfo的定义,(小组话题,问答等,有兴趣自己实现)
方法层定义一个blog的业务处理逻辑块:
var BlogInfo=require('../model/blogInfo'); exports.blog_save=function(newblog){ var blogInfo=new BlogInfo(); blogInfo.onSave(newblog); blogInfo.emitEvent(newblog); };
很简单的功能,就是创建博客实体对象,把新博客数据调用 onSave 方法让4个事件先监听好(4个观察者,坐等事件发生)
然后再调用 事件发射器把事件注册,(注意,一定要先又事件监听函数,然后出发事件,才有效果,否则事件发射结束,你无路如何也执行不了事件绑定函数,所以上面代码调用顺序绝对不能反)
app.js 代码启动文件
var http = require('http'); var blog= require('./methord/blog'); var webServer = function (req, res){ if(req.url!='/favicon.ico'){ var newblog={title:"标题",content:"内容"}; blog.blog_save(newblog); res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'}); res.write('<html><body>'); res.write('<h1>*** save blog ***</h1>'); res.write('<h2>Hello!</h2>'); res.end('</body></html>'); } }; var createWebServer=http.createServer(webServer); createWebServer.listen(8000); console.log('listen 8000');
一个不能再简单的node.js 服务器端实现,没有路由匹配…..此demo只在实现事件发射及相关绑定函数.
因为node.js默认一个request 总是会请求2次 why? 不明白的点链接查看.所以我在路由里简单加了个 if 判断.
不论什么请求,我们都能模拟一个 保存blog的情形.因为保存我们绑定了4个监听函数,然后触发保存方法的一刹那,我们会发射4个事件,这是事件观察者(监听函数)会以迅雷不及掩耳之势去捕捉,然后调用绑定函数帮我们实现了4个愿景….. 看下输出的结果吧:
控制台捕获事件的结果怎么样?
4个函数监听器各负其职,监听到了事件,而且成功执行了事件绑定函数.
*****************END*****************
原文: http://yijiebuyi.com/blog/1a8cb4bf8510f8e9267c2873b4cdda33.html
本文:Node.js: 如何继承 events 自定义事件及触发函数