sumeru
Version:
A Realtime Javascript RIA Framework For Mobile WebApp
335 lines (287 loc) • 12.8 kB
JavaScript
var runnable = function(fw,PublishContainer){
fw.addSubPackage('pubsub');
var subscribeMgr = {};//TODO 这个在server运行的时候,当并发很多(>10/s)时,可能出现后一个请求覆盖前一个引起callback未执行的bug,server渲染自动终止。这里需要重新设计
var publishModelMap = {a:1};
var subscribeMgrKeys = []; //有序的key记录,每个key都是一个pubname,用于断线重连后按顺序redo subscribe
var pubsubObject = function(){
};
var arrPop = Array.prototype.pop;
var arrSlice = Array.prototype.slice;
pubsubObject.prototype = {
subscribe : function(pubName, /*arg1, arg2, arg3*/ onComplete){
var _pubmap = publishModelMap[pubName.replace(/@@_sumeru_@@_page_([\d]+)/, '')];
var modelName = _pubmap['modelname'];
var plainStruct = _pubmap['plainstruct'];
if (typeof modelName == 'undefined') {
throw "publish " + pubName + ' NOT FOUND';
};
modelName = 'Model.' + modelName;
var env = null;
if ( typeof this === 'object' && typeof this.isWaiting !== 'undefined' ) {
env = this ;//this是env
env.wait();//自动调用wait方法
}
var completeCallback = arrPop.call(arguments);
var args = arrSlice.call(arguments,1);
var collection;
var cache = fw.cache.getPubData(pubName,args);
if(cache!=null){
try{
cache = JSON.parse(cache);
collection = fw.collection.create({modelName : modelName}, cache);
collection.pubName = pubName;
}catch(e){
collection = fw.collection.create({modelName : modelName});
collection.pubName = pubName;
}
}else{
collection = fw.collection.create({modelName : modelName});
collection.pubName = pubName;
}
//在collection上记了一下他是从哪个publish上来的
//send the subscribe netMessage
var version = collection.getVersion();
var id = this.__UK;
if(!subscribeMgr[pubName]){
subscribeMgrKeys.push(pubName);
subscribeMgr[pubName] = {
modelName : modelName,
plainStruct : plainStruct,
args : args,
topPriority : false, //是否在redo subscribe时要保证先进行
stub : []
};
}
//因为有再次订阅会两次返回,所以查看stub是否存在以后几个
//每次订阅都需要触发其他订阅的重新callback,如果是,这里补充env.wait
//否则不补充env.wait
for(var i=0,len = subscribeMgr[pubName].stub.length;i<len;i++){
if (subscribeMgr[pubName].stub[i].id === id){
env && env.wait();
}
}
var callbackStr = Function.toString.call(completeCallback);
var sourceCustomClosure = this.subscribe.caller;
// 清理相同env下,相同customClosure所做的订阅
subscribeMgr[pubName]['stub'] = subscribeMgr[pubName]['stub'].filter(function(item){
if(item.id == id && item.sourceCustomClosure == sourceCustomClosure && item.callbackStr == callbackStr){
return false;
}
return true;
});
//去重之后,包装原有callback,添加处理wait的方法
var tmpfunc = function( ){
try{
completeCallback.apply(undefined,arguments);
}catch(e){
console.warn("error when pubsub callback on line 84 \n" + e.stack || e);
}
if(env){
env.start();//自动调用start方法
}
};
subscribeMgr[pubName]['stub'].push({
id : id,
sourceCustomClosure : sourceCustomClosure,
collection : collection,
callback : tmpfunc,
callbackStr : callbackStr,
args : args,
env : this
});
var sendObj = {
name : pubName,
//去掉第一个pubname,去掉最后一个回调函数
args : args,
uk:id,
version : version
};
if (env && env.clientId){//from server controller
sendObj.clientId = env.clientId;
}
fw.netMessage.sendMessage(sendObj,'subscribe', function(err){
sumeru.log("error : subscribe " + err);
},function(){
sumeru.dev("send subscribe " + pubName, version || 'no version');
});
//for offline render中会执行callback,里面会用到return的 collection,所以需要延迟执行。--jin
if(cache!=null){
//collection.render();
setTimeout((function(c){return function(){c.render()}})(collection),0);
}
return collection;
//when data received from server, will run the onComplete
},
subscribeByPage : function(pubName, options, /*arg1, arg2,...*/ onComplete){
var defaultOptions = {
pagesize : 10,
page : 1,
uniqueField : 'time'
};
options = Library.objUtils.extend(defaultOptions, options);
var args = arguments;
//替换pubName
args[0] = pubName + '@@_sumeru_@@_page_' + options.page;
var collection = this.subscribe.apply(this, args);
return collection;
},
prioritySubscribe : function(pubName, /*arg1, arg2,...*/ onComplete){
var args = arguments;
var collection = this.subscribe.apply(this, args);
subscribeMgr[pubName].topPriority = true;
return collection;
}
};
/**
* 断线重连后,重做所有subcribe
*/
var redoAllSubscribe = function (){
redoAllPrioritySubscribe_(redoAllNormalSubscribe_);
};
var redoAllPrioritySubscribe_ = function(callback){
cbHandler = Library.asyncCallbackHandler.create(function(){
callback();
fw.pubsub.__reg('_priorityAsyncHandler', undefined, true);
});
fw.pubsub.__reg('_priorityAsyncHandler', cbHandler, true);
cbHandler.add();
//找出所有有优先级的订阅,发起并等待它们执行完毕
for(var i = 0, l = subscribeMgrKeys.length; i < l; i++){
var pubname = subscribeMgrKeys[i];
if (pubname == 'auth-init') { //auth-init由auth init负责调用,不需要再重复redo了。
continue;
};
if (!(pubname in subscribeMgr)) {
continue;
};
if (subscribeMgr[pubname].topPriority === false) {
continue;
};
cbHandler.add();
//redo all priority subscribe netMessage
var version = subscribeMgr[pubname].stub[0].collection.getVersion();
fw.netMessage.sendMessage({
name : pubname,
//去掉第一个pubname,去掉最后一个回调函数
args : subscribeMgr[pubname]['args'],
version : version
},'subscribe' , function(err){
fw.log("error : redoPrioritySubscribe", err);
} , function(){
fw.dev("sending redo priority subscribe " + pubname, version || "no version(redo)");
});
}
cbHandler.enableCallback();
cbHandler.decrease();
};
var redoAllNormalSubscribe_ = function(){
//执行所有普通订阅
for(var i = 0, l = subscribeMgrKeys.length; i < l; i++){
var pubname = subscribeMgrKeys[i];
if (pubname == 'auth-init') { //auth-init由auth init负责调用,不需要再重复redo了。
continue;
};
if (!(pubname in subscribeMgr)) {
continue;
};
if (subscribeMgr[pubname].topPriority === true) {
continue;
};
var version = subscribeMgr[pubname].stub[0].collection.getVersion();
//redo normal subscribe netMessage
fw.netMessage.sendMessage({
name : pubname,
//去掉第一个pubname,去掉最后一个回调函数
args : subscribeMgr[pubname]['args'],
version : version
},'subscribe' , function(err){
fw.log("Err : redoNormalSubscribe", err);
} , function(){
fw.dev("sending redo priority subscribe " + pubname, version || "no version(redo)");
});
}
};
fw.pubsub.__reg("clearClient",function(client_id){//server controller destroy
for(var pubName in subscribeMgr){
subscribeMgr[pubName]['stub'] = subscribeMgr[pubName]['stub'].filter(function(item){
if(item.id == client_id || !item.id){
return false;
}
return true;
});
if (subscribeMgr[pubName]['stub'].length == 0){
delete subscribeMgr[pubName];
}
}
});
fw.pubsub.__reg('clear',function(){
var item;
for(var pubName in subscribeMgr){
item = subscribeMgr[pubName].stub || {};
for(var key = item.length -1; key >= 0; key--){
if(item[key].env.isDestroy){
item[key].collection = null;
item[key].callback = null;
item[key].env = null;
item.splice(key,1);
};
}
if (item.length == 0) {
//FIXME 不用实时回收,因为很多情况下refresh会unsub然后又sub
//如果stub已经为空,通知Server,当前Client可以unsubscribe这个pubName了
fw.netMessage.sendMessage({
name : pubName
}, 'unsubscribe', function(err){}, function(){
fw.dev('unsubscribing', pubName);
});
delete subscribeMgr[pubName];
subscribeMgrKeys = subscribeMgrKeys.filter(function(item){
if (item == pubName) {
return false;
};
return true;
});
};
}
},true);
fw.pubsub.__reg('_pubsubObject', pubsubObject, true);
fw.pubsub.__reg('_subscribeMgr', subscribeMgr, true);
fw.pubsub.__reg('_publishModelMap', publishModelMap, true);
fw.pubsub.__reg('_redoAllSubscribe', redoAllSubscribe, true);
/**
* 延迟执行数据同步相关:
*/
fw.pubsub.__reg('_postponeQueue', [], 'private');
var postponeQueue = fw.pubsub._postponeQueue;
/**
* 释放后执行数据同步
*/
var releaseHold = function(collection){
if (postponeQueue.length === 0) {
return;
};
postponeQueue.forEach(function(item, index){
if (item.collection == collection) {
//执行后删除该记录
item.runner();
postponeQueue.splice(index, 1);
};
});
};
fw.pubsub.__reg('_releaseHold', releaseHold, 'private');
if (typeof PublishContainer !='undefined' ){//server no echo
(function(PublishContainer){
for (var pubname in PublishContainer){
publishModelMap[pubname] = {
'modelname' : PublishContainer[pubname]['modelName'],
'plainstruct' : PublishContainer[pubname]['plainStruct']
};
}
})(PublishContainer)
}
};
if(typeof module !='undefined' && module.exports){
module.exports = runnable;
}else{//这里是前端
runnable(sumeru);
}