最近在编写一个基于AJAX的WEB应用程序,常需要定时的对页面数据进行更新,这时候就需要用到JavaScript中的setTimeout、setInterval来进行定时的请求。虽然这两个内置的函数可以满足需求但使用上并不是那么便利,比如有个页面需要每10秒钟就更新一次,但从请求到返回可能不止10秒,这样就会造成前一个请求的响应还没接收到,第二个请求又开始了,这样会严重的浪费服务器和客户端的资源,所以必须在前一个请求响应完成后才开始第二次的请求。而有时候又需要暂停数据的更新,比如等待用户的一个操作。为了较方便的来实现这些需求便编写了一个定时器对象来达到这些需求,现在放出来给大家看看,有什么不足的地方希望大家可以指出。
实现功能:
- 基本定时器的功能,根据一个时间间隔执行表达式,可以取消定时
- 可以设定执行次数
- 可以选择先执行计时或定时器表达式
- 可以暂停,并由某个条件触发继续执行
定时器代码:
Code var Timer_State = { Start: 1, Stop: 2, Pause: 3};//expression 计时器要执行的脚本字符串//interval 执行间隔 单位毫秒//nMaxExeCount 执行次数 为 Number.POSITIVE_INFINITY 时不断执行//bFirstExe 为true时先执行表达式然后再进行下一次计时,为false时先执行下一次的计时再执行表达式function Timer(expression, interval, nMaxExeCount, bFirstExe) { if (Timer._Initialized == undefined) { //开始 Timer.prototype.Start = function() { if (this.State == Timer_State.Start) return; this.State = Timer_State.Start; this.doStart(); } Timer.prototype.doStart = function() { this.CreateExpressionTimer(); } Timer.prototype.CreateExpressionTimer = function() { var exp = this.BuildExpression(this.Expression); this._Timer = setTimeout(exp, this.Interval); } //表达式执行完毕 Timer.prototype.ExecuteComplete = function() { this.ExeCount++; if (this.MaxExeCount != Number.POSITIVE_INFINITY && this.ExeCount >= this.MaxExeCount) { this.Stop(); return; } if (this.State == Timer_State.Start) this.CreateExpressionTimer(); } //停止 Timer.prototype.Stop = function() { if (this.State == Timer_State.Stop) return; this.State = Timer_State.Stop; this.doStop(); } Timer.prototype.doStop = function() { this.ClearCheckPause(); if (this._Timer == null) return; clearTimeout(this._Timer); this._Timer = null; } //暂停 Timer.prototype.Pause = function(fCheckPause, checkInterval) { if (fCheckPause == null || typeof (fCheckPause) != "function") { throw "参数类型异常"; } if (this.State == Timer_State.Stop) return; this.ClearCheckPause(); this.State = Timer_State.Pause; this.CheckPauseHandler = fCheckPause; if (checkInterval == null) checkInterval = 500; this.CheckInterval = checkInterval; this.doStop(); this.doPause(); } Timer.prototype.doPause = function() { this._CheckPauseTimer = setTimeout("Timer.CheckPause(" + this.Id + ");", this.CheckInterval); } Timer.prototype.CheckPause = function() { if (this.CheckPauseHandler == null) return; if (this.CheckPauseHandler()) { this.Resume(); } else { this.doPause(); } } Timer.prototype.ClearCheckPause = function() { if (this._CheckPauseTimer == null) return; clearTimeout(this._CheckPauseTimer); this._CheckPauseTimer = null; this.CheckPauseHandler = null; } //恢复 Timer.prototype.Resume = function() { this.ClearCheckPause(); if (this.State == Timer_State.Stop) return; this.Start(); } Timer.prototype.BuildExpression = function(expression) { var str = "Timer.ExecuteComplete(" + this.Id + ");"; var _expression = this.FirstExe ? (expression + ";" + str) : (str + expression); return _expression; } //释放资源 Timer.prototype.Dispose = function() { this.Stop(); this.Expression = null; this.CheckPauseHandler = null; Timer.Timers.Remove(this); } } if (interval == null || expression == null) { throw "缺少参数"; } if (typeof (expression) != "string") { throw "参数类型不正确"; } if (nMaxExeCount == null) nMaxExeCount = Number.POSITIVE_INFINITY; if (bFirstExe == null) bFirstExe = false; this.MaxExeCount = nMaxExeCount; this.ExeCount = 0; this.FirstExe = bFirstExe; this.Interval = interval; this.State = Timer_State.Stop; this._Timer = null; this.CheckInterval = 500; this.CheckPauseHandler = null; this._CheckPauseTimer = null; this.Id = Timer.GetNewId(); this.Expression = expression; Timer.Timers.push(this);}//根据值取得索引Array.prototype.IndexOf = function(oValue) { var count = this.length, value; for (var i = 0; i < count; i++) { if (this[i] == oValue) { return i; } } return -1;}//根据索引移除Array.prototype.RemoveByIndex = function(iStartIndex, iDeleteCount) { if (iDeleteCount == null) iDeleteCount = 1; this.splice(iStartIndex, iDeleteCount); return this.length;}//移除项Array.prototype.Remove = function(oValue) { var index = this.IndexOf(oValue); return this.RemoveByIndex(index);}Timer.Timers = new Array();Timer.LastId = 0;Timer.GetNewId = function() { ++Timer.LastId; return Timer.LastId;}Timer.GetTimerById = function(id) { var count = Timer.Timers.length; for (var i = 0; i < count; i++) { var timer = Timer.Timers[i]; if (timer.Id == id) { return timer; } } return null;}Timer.ExecuteComplete = function(id) { var timer = Timer.GetTimerById(id); if (timer != null) timer.ExecuteComplete();}Timer.CheckPause = function(id) { var timer = Timer.GetTimerById(id); if (timer != null) timer.CheckPause();}
以上是完整的代码,下面说说如何使用这个Timer对象。
首先要实例化一个Timer对象。如:
var timer = new Timer( " show(); " , 1000 , Number.POSITIVE_INFINITY, false );
Timer这个对象有4个参数。
第一个参数expression, 用于设定定时器到达时间间隔时要执行的表达式。在上面的示例中值为"alert(new Date().toLocaleTimeString())"
第二个参数interval,用于设定定时器的时间间隔,单位为毫秒。在上面的示例中值为3000,也就是3秒
这两个参数用过setTimeout、setInterval的人应该很熟悉。
第三个参数nMaxExeCount,用于设定最大的执行次数,达到这个数之后就不再执行了,当该参数值为Number.POSITIVE_INFINITY时会不断的执行,相当于setInterval的功能。此参数默认值为Number.POSITIVE_INFINITY。在上面的示例中值为1,也就是只执行一次,相当于setTimeout。
第四个参数bFirstExe,用于设定定时器的执行顺序,当值为true时会先执行表达式脚本然后再进行下一次的计时,当值为false时会先执行下一次的计时然后再执行表达式脚本。在上面的示例中值为true,也就是先执行表达式脚本。
对于第四个参数我举个例子说明下:
假设我将Timer设定为3秒执行一次,而执行表达式脚本所需要的时间为5秒,这样当参数bFirstExe为true的时候实际上第一次执行和第二次执行的时间间隔为8秒,而当参数bFirstExe为false的时候第一次执行和第二次执行的时间间隔为5秒,也就是取执行间隔和执行所需要时间这两个值的最大值。实际上在第一次执行前已经开始了第二次执行的计时动作,但由于JS是单线程的,无法实现异步,所以他会等待第一次的表达式执行完毕后才会执行第二次。
实例化完后需要调用Start()来启动定时器,这样定时器才会开始工作。如:
当需要停止定时器时就调用Stop(),这样定时器就会停止,不再执行。如:
而需要暂停定时器并根据一个条件再度启动时就调用Pause(),这样定时器会暂停执行,并不断调用由参数一传递来的函数checkPause直到函数返回一个true使定时器继续执行,调用的时间间隔为第二个参数的值,单位为毫秒,默认值为500毫秒。如:
timer.Pause(checkPause, 1000 );
也可以调用Resume()手动恢复暂停,使定时器继续执行。如:
当此定时器不再使用的时候也可以调用Dispose(),来释放资源,将其从定时器列表中移除掉。如:
执行过程中也可以修改Timer的属性来改变表达式、调用间隔、最大执行次数等。如:
timer.Stop(); // 停止定时器 timer.ExeCount = 0 ; // 执行次数归零 timer.MaxExeCount = 5 ; // 改变最大执行次数 timer.FirstExe = false ; // 改变执行顺序 timer.Interval = 2000 ; // 改变执行间隔 timer.Expression = " show2() " ; // 改变表达式 timer.Start(); // 启动定时器
使用中会用到的属性和方法都介绍完了,附带个示例给大家测试看看:
转自