什么是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
定時器的原理也很簡單,這里不作介紹,有興趣的可以自己研究下,也歡迎修改該定時器源碼,使之更迅速和安全。