node.js - events
event - 事件 node.js 使用事件驱动 EventEmitter() 这是基础class 创建:
// import events module
const events = require('events');
// create event emitter instance
let emitter = new events.EventEmitter();
// on off emit once
// on -> emit
emitter.on('fnon',() => {
consle.log('hello on')
})
emitter.emit('fnon')
emitter.emit('fnon')
// 'hello on'
// 'hello on'
// once -> emit
emitter.once('fnonce',() => {
consle.log('hello once')
})
emitter.emit('fnon')
emitter.emit('fnon')
// 'hello on' <- only once
emitter.on('fnon',() => {
consle.log('hello on')
})
emitter.off('fnon')
emitter.emit('fnon')
// 无输出
im - qq-adapter 实现
实现六个方法
- create() 调用sdk中的create()初始化创建实例,之后setLogLevel(0) 应该是日志或者相关配置
- destroy() 销毁sdk实例
- login()登录 调用sdk.login(accid,token)成功以后返回{ success: true, code: ‘0’, msg: ‘IM-login success’, data: res }
- logout()登出 直接调用sdk.logout()
- on() 订阅sdk事件 eventname的case三个选择: 1.MESSAGE_RECEIVED: 接受触发的msgData,并且遍历每一条消息,如果是TIMTextElem并且不为空就格式映射为:
type:'TextElem',
text:item.payload.text
并回调给上层的监听器index中listener。
on(eventname, listener) {
this._emitter.on(eventname, listener);
}
如果消息类型为TIMCustomElem并且不为空就映射为type:'CustomElem',
data:item.payload.data,
time:item.time,
from:item.from,
同上 该部分首先被sdk触发之后进入回调,处理以后抛给上层的index中on订阅的listener,然后那边emit事件。 职责:订阅->读取->归一化->上抛 2.KICKED_OUT: SDK 触发 KICKED_OUT 事件,并返回回调参数event,根据event.data.type的值对应到_kickOutTypeMap中映射的_kickOutTypeMap,相关映射见上面,最后将适配器映射的_kickOutTypeMap回调上层的listener。 职责:订阅->读取->映射->上抛 3.NET_STATE_CHANGE: sdk触发NET_STATE_CHANGE事件,返回event,根据event.data.state对应_netStateMap映射为对应的值,之后回调listener。 职责同上:订阅->读取->映射->上抛 4.default break: 未匹配,什么也不做
- off() 取消订阅sdk事件
小结
im中的adapter职责:
- 初始化实例与配置 create()
- 销毁SDK实例 destroy()
- 登录与登出 login/logout
- 订阅监听器(职责为订阅->读取->映射/归一化->上抛)
- 移除监听器
Q: adapter与index? on与emint?到底在做什么?
A: 首先index把回调函数作为参数传递给了adapter 之后sdk事件发生时候,adapter调用这个回调函数,把归一化后的处理结果上抛给index 然后index在自己的回调中再emit转发
生命周期(index): create->on login off->logout->destory 逐一分析,首先是create,
// 处理统一参数、切换sdk、创建IM实例等
create(options: IMConfig) {
// 根据不同的channel,切换不同的sdk
const imModules = loadIMModules<IMAdapter>();
if (!imModules[IMCont.imChannel]) {
clog.error('IM-', `当前IM不支持${IMCont.imChannel}渠道`);
return false;
}
// 设置IM统一数据层数据
this._setInitCont(options);
this._imAdapter = imModules[IMCont.imChannel];
this._imAdapter.create({
appid:IMCont.appid,
proxyServer:IMCont.proxyServer,
});
this._addListener();
return true;
}
由此可以发现,在初始化实例并创建完成后调用了_addListener()
private _addListener() {
console.log('this._imAdapter',this._imAdapter.EVENT);
this._imAdapter.on(this._imAdapter.EVENT.MESSAGE_RECEIVED, (item) => {
// 过滤登录之前的消息
if(this._loginTime > item.time){
clog.log('IM-Filtration message', item);
return;
}
if (item.type == 'TextElem') {
//文字聊天
if (item.text != '') {
this._emitter.emit(this.EVENT.TEXT_CHAT, item.text);
}
}
if (item.type == 'CustomElem') {
//自定义消息
if (item.data != '') {
this._emitter.emit(this.EVENT.RECEIVED_MESSAGE, item);
}
}
});
因此实例创建完成以后,马上订阅事件。 login单独触发,里面也是正常调用login()正常用。 logout如下:
logout() {
this._removeListener();
// 再次登录时需要 imChannel 值
// IMCont.resetParameters();
return this._imAdapter.destroy();
}
先是调用了_removeListener()
private _removeListener() {
this._imAdapter.off(this._imAdapter.EVENT.MESSAGE_RECEIVED, () => { });
this._imAdapter.off(this._imAdapter.EVENT.KICKED_OUT, () => { });
this._imAdapter.off(this._imAdapter.EVENT.ERROR, () => { });
}
我们可以发现,在内部逐个取消订阅的监视器,而后destroy(),销毁实例。先退订再销毁。