什么是jQuery timer?
答:一个使用Jquery扩展而成的定时器类的插件。
为什么要这样的插件?
答: 更加灵活方便使用javascript定时器。
以下是插件代码,根据一款timer定时器类实现,只不过这里直接使用jQuery集成。
github:https://github.com/viticm/jquery.timer 源文件:jquery.timer.dev.js
代码:
/**
* @package jQuery Timer
* @desc this plugin for jQuery of Timer as based from jQuery ui plugin frame
* @date 2013-5-10
* @version 1.0.1
* @author viticm
*/
( function( $, undefined )
{
$.timer = $.timer || {};
$.timer.console = $.timer.console || {}; // debug
if( 'undefined' == typeof console ) // if not found client object then use default output function
{
$.extend( $.timer.console,
{
error: function( cErrorStr )
{
alert( '[ERROR] '+cErrorStr );
return;
},
warn: function( cWarningStr )
{
alert( '[WARNING] '+cWarningStr );
},
log: function( cLogStr )
{
alert( '[LOG] '+cLogStr );
}
});
}
else
{
$.timer.console = console; // notice: this( console ) object can't extend
}
$.extend( $.timer,
{
version: '1.0.1',
options:
{
iTimerDelay: 1000,
iRepeatCount: 10,
iCurrentCount: 0,
bRunning: false,
cRepeatType: null == this.iRePeatCount || 1 > this.iRepeatCount ? 'interval' : 'timeout',
bCompleted: false,
timerEventRun: { bBubbles: false, bCancleable: false },
timerEventComplete: { bBubbles: false, bCancleable: false },
funcListener: null,
bDebug: false,
name: '',
OBJ_StartDate: null,
userData: {},
},
iTimerId: 0,
OBJ_TimerEventRun: null,
OBJ_TimerEventComplete: null,
handler: {},
});
$.extend( $.timer,
{
init: function( options )
{
$.extend( this.options, this.options, options || {} );
this.OBJ_TimerEventRun = this.timerEvent.init( this.timerEventRun );
this.OBJ_TimerEventComplete = this.timerEvent.init( this.timerEventComplete );
var Arr_ListenerMap = []; //hanler listener map
Arr_ListenerMap[ this.timerEvent.TIMER ] = [];
Arr_ListenerMap[ this.timerEvent.TIMER_COMPLETE ] = [];
this.handler = Arr_ListenerMap;
return $.extend( true, {}, this );
},
});
$.extend( $.timer,
{
timerEvent:
{
cType: 'timer',
bBubbles: false,
bCancleable: false,
init: function( timerEventSet )
{
var timerEventSet = 'undefined' === typeof timerEventSet ? {} : timerEventSet;
this.cType = undefined === timerEventSet.cType ? this.cType : timerEventSet.cType;
this.bBubbles = timerEventSet || undefined === timerEventSet.bBubbles ? this.bBubbles : timerEventSet.bBubbles;
this.bCancleable = undefined === timerEventSet.bCancleable ? this.bCancleable : timerEventSet.bCancleable;
return this;
},
TIMER: 'timer',
TIMER_COMPLETE: 'timerComplete',
toString: function()
{
return '[timerEvent type='+this.cType
+' bubbles='+this.bBubbles
+' cancleable='+this.bCancleable
+']';
}
}
});
// listeners
$.extend( $.timer,
{
addEventListener: function( cType, funcListener, bUseCapture )
{
if( this.timerEvent.TIMER == cType || this.timerEvent.TIMER_COMPLETE == cType )
{
if( !funcListener && true === this.options.bDebug )
$.timer.console.warn( 'not found listener function! timer name: ' + this.options.name );
if( true === bUseCapture )
{
this.handler[ cType ].splice( 0, 0, [ funcListener ] );
}
else
{
this.handler[ cType ].push( funcListener );
}
}
},
removeEventListener: function( cType, funcListener )
{
if( this.timerEvent.TIMER === cType || this.timerEvent.TIMER_COMPLETE === cType )
{
if( !funcListener )
{
this.handler[ cType ] = [];
}
else
{
var Arr_TypeListener = this.handler[ cType ];
for( var index = 0; index < Arr_TypeListener; index++ )
{
if( funcListener === Arr_TypeListener[ index ] )
{
Arr_TypeListener.splice( index, 1 );
break;
}
}
}
}
},
// delay function for time out
delayExecute: function( Arr_Listener )
{
var OBJ_TimerThis = this;
this.dispatchListener( Arr_Listener, this.OBJ_TimerEventRun );
this.options.iCurrentCount++;
if( this.options.iCurrentCount < this.options.iRepeatCount )
{
if( true === this.options.bRunning )
{
this.iTimerId = setTimeout( function()
{
OBJ_TimerThis.delayExecute( Arr_Listener );
}, this.options.iTimerDelay );
}
}
else
{
this.options.bRunning = false;
}
if( false === this.options.bRunning )
{
if( false === this.options.bCompleted )
{
this.dispatchListener( this.handler[ this.timerEvent.TIMER_COMPLETE ],
this.OBJ_TimerEventComplete );
this.options.bCompleted = true;
}
}
},
// normal do listener function
dispatchListener: function( Arr_Listener, OBJ_TimerEvent )
{
for( var prop in Arr_Listener )
{
Arr_Listener[ prop ]( OBJ_TimerEvent );
}
}
});
// actions
$.extend( $.timer,
{
start: function()
{
var OBJ_TimerThis = this;
if( true === this.options.bRunning || true === this.options.bCompleted )
return;
if( 0 === this.handler[ this.timerEvent.TIMER ].length
&& 0 === this.handler[ this.timerEvent.TIMER_COMPLETE ].length )
{
this.console.warn( 'not found listener function! timer name: ' + this.options.name );
return;
}
this.options.bRunning = true;
this.options.OBJ_StartDate = new Date();
if( 'timeout' == this.options.cRepeatType )
{
this.iTimerId = setTimeout( function()
{
OBJ_TimerThis.delayExecute( OBJ_TimerThis.handler[ OBJ_TimerThis.timerEvent.TIMER ],
OBJ_TimerThis.OBJ_TimerEventRun );
}, this.options.iTimerDelay );
}
else
{
this.iTimerId = setInterval( function()
{
OBJ_TimerThis.dispatchListener( OBJ_TimerThis.handler[ OBJ_TimerThis.timerEvent.TIMER ],
OBJ_TimerThis.OBJ_TimerEventRun );
}, this.options.iTimerDelay );
}
},
stop: function()
{
if( null === this.iTimerId ) return;
if( 'timeout' == this.options.cRepeatType )
{
clearTimeout( this.iTimerId );
this.options.bRunning = false;
}
else
{
clearInterval( this.iTimerId );
}
if( false === this.options.bCompleted )
{
this.dispatchListener( this.handler[ this.timerEvent.TIMER_COMPLETE ],
this.OBJ_TimerEventComplete );
}
this.options.bCompleted = true;
},
reset: function()
{
this.options.iCurrentCount = 0;
this.options.bRunning = true;
this.options.bCompleted = false;
this.options.OBJ_StartTime = new Date();
}
});
// extend functions
$.extend( $.timer,
{
getRunTime: function()
{
var OBJ_NowDate = new Date();
if( !this.options.OBJ_StartDate ) return false;
return OBJ_NowDate.getTime() - this.options.OBJ_StartDate.getTime();
}
});
})( jQuery );
该插件让定时器归类管理,而且可以灵活根据不同的情况使用timeout或者interval类型。
options中的参数更灵活,甚至可以使用其中的扩展userData来添加监听方法中需要使用的变量方法,不再担心全局变量而使代码混乱。
一个实例,充分的利用该插件进行批量数据操作:
因为手头有个功能需要合并数据表,这个在游戏合区或者合并某些网站数据时都需要,如果用单纯的页面,在数据量比较大的情况下,可能导致程序运行时长过长,浏览器卡死。而且于过程更是无法掌控,无法看到具体运行的情况。这个时候我们可以利用定时器来操作了,虽然有些服务器脚本语言也能实现,但是往往都非常麻烦。如果用在客户端的话,这就比较容易了,而且还能动态看到数据的不停更新,这也正是前端语言 javascript的好处之一。
图例:(◇表示复选框未选中 ◆表示复选框已选中)
数据库一:
◇ 表一 ◆ 表二
数据库二:
◆ 表一 ◆ 表二
我们的需要是把数据库一和二选中的数据表中数据复制到目标数据库中。
页面实现代码(html),由于涉及到项目代码原因,这里这给出js部分,其实大致也就这么多了:
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>合服工具</title>
<script type="text/javascript" src="../static/js/jquery.min.js"></script>
<script type="text/javascript" src="../static/js/commonly_for_my.js"></script>
<script type="text/javascript" src="../static/js/jquery.timer.dev.js"></script>
<script type="text/javascript" >
$(document).ready( function(){
$( "#allAdminTables" ).click( function(){
$( "#divAdminTables :checkbox" ).attr( "checked", $(this).prop( "checked" ) );
});
$( "#allGameTables" ).click( function(){
$( "#divGameTables :checkbox" ).attr( "checked", $(this).prop( "checked" ) );
});
//复选框对齐
$( "ul label" ).each( function(){
$(this).wrap( "<li></li>" );
});
/** run merge servers **/
iDstServerId = $( '#dstServerId' ).val();
$("#btnSubmit").click( function()
{
if ( "" == iDstServerId || null == iDstServerId )
{
alert( '目标服务器不存在,无法合区' );
return;
}
msg = "你要合并的是 <{$CURRENT_SERVER_NAME}> 服,合并到服务器: <{$AGENT_NAME}>s"+iDstServerId;
msg += "\n注意!合区后目标服务器数据将被更新,请停服备份后再进行此操作。否则会出现严重后果!!!!";
if( !confirm( msg ) )
{
return false;
}
else
{
var Arr_AdminTableId = getCheckBoxByName( 'Arr_AdminTableName[]' );
var Arr_GameTableId = getCheckBoxByName( 'Arr_GameTableName[]' );
var Arr_ServerTableId = []; // all server table id
var Arr_DbType = [];
var DB_ADMIN, DB_GAME;
var Arr_ServerDbName = []; // all server db name
var iCopyOnceRecords = 30; // the once copy table records
var iTimerDelay = 300; // copy table interval
DB_ADMIN = 0;
DB_GAME = 1;
Arr_ServerTableId[ DB_ADMIN ] = Arr_AdminTableId;
Arr_ServerTableId[ DB_GAME ] = Arr_GameTableId;
Arr_DbType[ DB_ADMIN ] = 'admin';
Arr_DbType[ DB_GAME ] = 'game';
Arr_ServerDbName[ DB_ADMIN ] = '后台';
Arr_ServerDbName[ DB_GAME ] = '游戏';
$( '#tipTable' ).css( 'display', '' ); // show tips start
if( 0 === Arr_AdminTableId.length && 0 === Arr_GameTableId.length )
{
showTips( 'error', '对不起,至少要选择一张表进行操作' );
return;
}
// default timer type: interval
var OBJ_MergeServerDbTimer = $.timer.init
({
iTimerDelay: iTimerDelay,
bDebug: true,
name: 'merge server',
userData:
{
Arr_ServerTableId: Arr_ServerTableId,
Arr_ServerDbName: Arr_ServerDbName,
Arr_DbType: Arr_DbType,
tableInfo: null, // JSON Object { 'tableName': 'test', 'records': 0 } or false
iDstServerId: iDstServerId,
iStartMergeDbIndex: 0,
iCurrentMergeDbIndex: 0,
iEndMergeDbIndex: 1,
iCurrentMergeTableIndex: 0,
iCurrentMergeTableMergedRecords: 0,
iCopyOnceRecords: iCopyOnceRecords,
bDbRunMerge: false,
bTableRunMerge: false,
bRunError: false,
bMergeCompleted: false,
}
});
// add run listener function
var funcMergeServerDbTimerRunListener = function()
{
var OBJ_UserData = OBJ_MergeServerDbTimer.options.userData;
if( true === OBJ_UserData.bMergeCompleted ){ OBJ_MergeServerDbTimer.stop(); return; } //if completed then stop
var Arr_DbTableId = OBJ_UserData.Arr_ServerTableId[ OBJ_UserData.iCurrentMergeDbIndex ];
// check merge db
if( 0 === Arr_DbTableId.length && OBJ_UserData.iCurrentMergeDbIndex < OBJ_UserData.iEndMergeDbIndex ) OBJ_UserData.iCurrentMergeDbIndex++;
if( false == OBJ_UserData.bDbRunMerge )
{
showTips( 'success', '开始合并: [' +OBJ_UserData.Arr_ServerDbName[ OBJ_UserData.iCurrentMergeDbIndex ]+ ']数据库......' );
OBJ_UserData.bDbRunMerge = true;
if( 0 === Arr_DbTableId.length )
{
showTips( 'success', '数据库: [' +OBJ_UserData.Arr_ServerDbName[ OBJ_UserData.iCurrentMergeDbIndex ]+ ']没有要合并的表' );
runNextMerge();
return true;
}
}
if( false === OBJ_UserData.bTableRunMerge )
{
// get table records and name
var OBJ_TableInfo = null;
OBJ_TableInfo = getTableInfo( OBJ_UserData.Arr_DbType[ OBJ_UserData.iCurrentMergeDbIndex ],
Arr_DbTableId[ OBJ_UserData.iCurrentMergeTableIndex ] );
if( false === OBJ_TableInfo )
{
errorStopTimer();
}
else
{
OBJ_UserData.bTableRunMerge = true;
OBJ_UserData.tableInfo = OBJ_TableInfo;
}
}
// start merge table
if( true === OBJ_UserData.bTableRunMerge )
{
var OBJ_TableInfo = OBJ_UserData.tableInfo;
if( 0 === OBJ_TableInfo.records )
{
showTips( 'success', '数据表: [' +OBJ_TableInfo.tableName+ '] 记录为空, 总耗时: ' + OBJ_MergeServerDbTimer.getRunTime() / 1000 + 's' );
runNextMerge();
return true;
}
else
{
// this code like do ... while ... , at least run once merge table function
var OBJ_MergeTableRequest = null;
var iMergeTableRecords =
OBJ_TableInfo.records < OBJ_UserData.iCurrentMergeTableMergedRecords + OBJ_UserData.iCopyOnceRecords
? OBJ_TableInfo.records : OBJ_UserData.iCurrentMergeTableMergedRecords + OBJ_UserData.iCopyOnceRecords;
showTips( 'success', '正在合并数据表: [' +OBJ_TableInfo.tableName+ '] ' +iMergeTableRecords+ '/' +OBJ_TableInfo.records+
'总耗时: ' + OBJ_MergeServerDbTimer.getRunTime() / 1000 + 's' );
OBJ_MergeTableRequest = mergeTable(
OBJ_UserData.iDstServerId,
OBJ_UserData.Arr_DbType[ OBJ_UserData.iCurrentMergeDbIndex ],
Arr_DbTableId[ OBJ_UserData.iCurrentMergeTableIndex ],
OBJ_UserData.iCurrentMergeTableMergedRecords,
OBJ_UserData.iCopyOnceRecords ); // merge table
if( false === OBJ_MergeTableRequest )
{
errorStopTimer();
}
else
{
OBJ_UserData.iCurrentMergeTableMergedRecords += OBJ_UserData.iCopyOnceRecords;
if( OBJ_TableInfo.records < OBJ_UserData.iCurrentMergeTableMergedRecords )
{
runNextMerge();
return true;
}
}
}
}
/**
* @desc get table records and name by table id and db type
* @param int iTableId table id
* @param string cDbType db type
* @return bool|Object
*/
function getTableInfo( cDbType, iTableId )
{
var cRequestMethod = 'POST';
var cRequestUrl = '<{$REQUEST_URI}>';
var cSendData = 'action=getTableRecords&dbType=' +cDbType+ '&tableId=' +iTableId;
return getAjaxRequest( cRequestMethod, cRequestUrl, cSendData );
}
/**
* @desc copy table records to dst server
* @param int iDstServerId int dst server id
* @param string cDbType db type
* @param int iTableId table id
* @param int iOffsetStart merge records start offset
* @param int iOnceRecords merge table once copy records
* @return bool|Object
*/
function mergeTable( iDstServerId, cDbType, iTableId, iOffsetStart, iOnceRecords )
{
var cRequestMethod = 'POST';
var cRequestUrl = '<{$REQUEST_URI}>';
var cSendData = 'action=copyTable&dstServerId=' +iDstServerId+ '&dbType='+cDbType+ '&tableId=' +iTableId
+ '&offsetStart=' +iOffsetStart+ '&records=' +iOnceRecords;
return getAjaxRequest( cRequestMethod, cRequestUrl, cSendData );
}
/**
* @desc stop merge server timer use for run have some error
* @param void
* @return void
*/
function errorStopTimer()
{
OBJ_UserData.bRunError = true;
OBJ_UserData.bTableRunMerge = false;
OBJ_MergeServerDbTimer.stop();
}
/**
* @desc run next run merge db or table
* @param void
* @return void
*/
function runNextMerge()
{
// next table or db
if( OBJ_UserData.iCurrentMergeTableIndex + 1 < Arr_DbTableId.length ) // array key begin from zero
{
OBJ_UserData.iCurrentMergeTableIndex++;
OBJ_UserData.bTableRunMerge = false;
OBJ_UserData.iCurrentMergeTableMergedRecords = 0;
}
else
{
OBJ_UserData.iCurrentMergeDbIndex++;
OBJ_UserData.bDbRunMerge = false;
OBJ_UserData.bTableRunMerge = false;
OBJ_UserData.iCurrentMergeTableMergedRecords = 0;
OBJ_UserData.iCurrentMergeTableIndex = 0;
if( OBJ_UserData.iCurrentMergeDbIndex > OBJ_UserData.iEndMergeDbIndex ) // merge db is completed
OBJ_UserData.bMergeCompleted = true;
}
}
};
var funcMergeServerDbTimerCompleteListener = function()
{
var OBJ_UserData = OBJ_MergeServerDbTimer.options.userData;
if( false === OBJ_UserData.bRunError )
{
showTips( 'success', '数据合并成功,正在写入合区日志' );
if( false !== addMergeLog() ) //merge server log
showTips( 'success', '合区成功! 共耗时为: '+OBJ_MergeServerDbTimer.getRunTime() / 1000+ 's' );
}
if( true === OBJ_MergeServerDbTimer.options.bDebug )
OBJ_MergeServerDbTimer.console.log( 'Timer: [' +OBJ_MergeServerDbTimer.options.name+
'] is complete or stop, run time: ' +OBJ_MergeServerDbTimer.getRunTime()+ 'ms' );
/**
* @desc add merge log
* @param void
* @return bool|Object
*/
function addMergeLog()
{
var cRequestMethod = 'POST';
var cRequestUrl = '<{$REQUEST_URI}>';
var cSendData = 'action=addMergeLog&dstServerId=' +OBJ_UserData.iDstServerId;
return getAjaxRequest( cRequestMethod, cRequestUrl, cSendData );
}
};
OBJ_MergeServerDbTimer.addEventListener( 'timer', funcMergeServerDbTimerRunListener );
OBJ_MergeServerDbTimer.addEventListener( 'timerComplete', funcMergeServerDbTimerCompleteListener );
OBJ_MergeServerDbTimer.start();
}
});
});
</script>
</head>
其中commonly_for_my.js代码,这个只是个人临时用的js:
/*!
* My ui
* @desc this js is commonly used of javascript func for myself
* @author viticm<viticm@126.com>
* @date 2013-4-23
*/
/**
* @desc 通过名称获得复选框值数组
* @param string cCheckBoxName
* @returns array
*/
function getCheckBoxByName( cCheckBoxName )
{
var Arr_CheckBoxVal = [];
$( "input[name='"+cCheckBoxName+"']:checked" ).each(function(index, element) {
Arr_CheckBoxVal.push( $(this).val() )
});
return Arr_CheckBoxVal;
}
/**
* @desc 通用小提示
* @param string cType
* @param string cContent
* @returns array
*/
function showTips( cType, cContent )
{
cTipHtml = '';
cColor = 'error' == cType ? 'red' : 'green';
cTipHtml = '<span style="color:'+cColor+';">'+cContent+'</span>';
$( '#tips' ).html( cTipHtml );
}
/** 记住javscript中的数字和字符串的比较 **/
Array.prototype.max = function()
{
var max = parseInt( this[ 0 ] );
var len = this.length;
var maxIndex = 0;
if ( 0 == len ) return undefined;
for (var i = 1; i < len; i++)
{
var nowVal = parseInt( this[ i ] );
if( max < nowVal )
{
max = nowVal;
maxIndex = i;
}
}
return maxIndex;
}
/**
* @desc get ajax return
* @param string cRequestMethod http request method( POST OR GET )
* @param string cRequestUrl get url
* @param string cSendData the url param string
* @returns bool|Object
*/
function getAjaxRequest( cRequestMethod, cRequestUrl, cSendData )
{
var iHttpRequestStatus = 0;
var OBJ_RequestJson = null;
$.ajax({
type: cRequestMethod,
url: cRequestUrl,
data: cSendData,
async: false,
success: function( data )
{
OBJ_RequestJson = $.parseJSON( data );
},
error: function( OBJ_XMLHttpRequest )
{
iHttpRequestStatus = OBJ_XMLHttpRequest.status;
}
});
if( 0 !== iHttpRequestStatus ) showTips( 'error', 'Request failed, Error code: ' +iHttpRequestStatus );
// ErrorCode is a compatible variable just for mine, you can del it.
if( null !== OBJ_RequestJson && 'undefined' !== typeof OBJ_RequestJson.ErrorCode && 1 != OBJ_RequestJson.ErrorCode )
showTips( 'error', 'Url request have some error, Error desc: ' +decodeURIComponent( OBJ_RequestJson.ErrorDesc ) );
return 0 !== iHttpRequestStatus || ( 'undefined' !== typeof OBJ_RequestJson.ErrorCode && 1 != OBJ_RequestJson.ErrorCode )
? false : OBJ_RequestJson;
}
timer的用法很简单,需要$.timer.init( {} )初始化后,然后对这个对象进行操作。你可以将其中的bDebug设置为true,那么在出现错误或者运行中的日志都可以输出到浏览器。如果有控制台,一般F12呼出,则可以在控制台中看到这些运行信息,如果没有则将运行默认的alert()方式运行提示。
常用事件说明:
var OBJ_Timer = $.timer.init //初始化,如果不传参数则以默认方式
( {
iTimerDelay: 1000, // 定时器间隔
iRepeatCount: 10, // 循环次数,定时器类型为timeout有效
cRepeatType: 'interval', // 定时器类型:timeout|interval
bDebug: false, // 是否启用调试
name: '', // 定时器名称
userData: {}, // 用户数据
} );
OBJ_Timer.addEventListener( cType, funcListener, bUseCapture ); /** 添加监听事件方法 cType 监听类型(timer(运行中)|timerComplete(运行结束)) funcListener 监听方法 bUseCapture 是否插到所有监听事件开头 **/
OBJ_Timer.removeEventListener( cType, funcListener ); /** 移除监听事件 cType 监听类型(timer(运行中)|timerComplete(运行结束)) funcListener 监听方法 **/
OBJ_Timer.start(); //开始定时器
OBJ_Timer.stop(); //停止定时器
OBJ_Timer.reset(); //重置定时器
OBJ_Timer.getRunTime(); //获得定时器运行时间 单位 ms
定时器的原理也很简单,这里不作介绍,有兴趣的可以自己研究下,也欢迎修改该定时器源码,使之更迅速和安全。