使用setTimeout或setInterval时遇到的问题总结

写定时器分两种写法,setInterval和setTimeout。
先来说setInterval:
setInterval(code,millisec[,"lang"])
setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。
setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。
由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。
再来说说setTimeout:
setTimeout(code,millisec)
setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式。
setTimeout() 只执行 code 一次。如果要多次调用,请使用 setInterval() 或者让 code 自身再次调用 setTimeout()。
通过分析可以得出一个结论,如果要做一个定时器的应用,setInterval可以使用匿名函数实现,setTimeout却不可以。例如:
  1. window.onload = function(){  
  2.     var oC = document.getElementById('cv'),  
  3.         oGC = oC.getContext('2d'),  
  4.         width = 100,  
  5.         height = 100,  
  6.         step = 0;  
  7.   
  8.     oGC.fillStyle = 'rgba(0,255,255,1)';  
  9.     oGC.fillRect(0,0,width,height);  
  10.   
  11.     setInterval(function(){  
  12.         oGC.clearRect(0,0,oC.width,oC.height);  
  13.         oGC.fillRect(step,step,width,height);  
  14.         step++;  
  15.     },30);  
  16. }  
如果使用setTimeout实现定时器,则需要首先定义一个函数,之后使用 setTimeout() 调用该函数,最后在该函数中再次调用 setTimeout()。
如果按照这个逻辑代码应该是这样的:
  1. window.onload = function(){  
  2.     var oC = document.getElementById('cv'),  
  3.         oGC = oC.getContext('2d'),  
  4.         width = 100,  
  5.         height = 100,  
  6.         step = 0;  
  7.   
  8.     oGC.fillStyle = 'rgba(0,255,255,1)';  
  9.     oGC.fillRect(0,0,width,height);  
  10.   
  11.     function redraw(){  
  12.         oGC.clearRect(0,0,oC.width,oC.height);  
  13.         oGC.fillRect(step,step,width,height);  
  14.         step++;  
  15.         setTimeout("redraw()",30);  
  16.     }  
  17.   
  18.     setTimeout("redraw()",30);  
  19. }  
运行后却发现有错误:Uncaught ReferenceError: redraw is not defined。
如果运行代码没在 window.onload = function(){}环境下,即把 window.onload = function(){} 方法去除:
  1. var oC = document.getElementById('cv'),  
  2.     oGC = oC.getContext('2d'),  
  3.     width = 100,  
  4.     height = 100,  
  5.     step = 0;  
  6.   
  7. oGC.fillStyle = 'rgba(0,255,255,1)';  
  8. oGC.fillRect(0,0,width,height);  
  9.   
  10. function redraw(){  
  11.     oGC.clearRect(0,0,oC.width,oC.height);  
  12.     oGC.fillRect(step,step,width,height);  
  13.     step++;  
  14.     setTimeout("redraw()",30);  
  15. }  
  16. setTimeout("redraw()",30);  
这样错误就消失了,为什么呢?如果换成setInterval调用呢?
  1. window.onload = function(){  
  2.     var oC = document.getElementById('cv'),  
  3.         oGC = oC.getContext('2d'),  
  4.         width = 100,  
  5.         height = 100,  
  6.         step = 0;  
  7.   
  8.     oGC.fillStyle = 'rgba(0,255,255,1)';  
  9.     oGC.fillRect(0,0,width,height);  
  10.   
  11.     function redraw(){  
  12.         oGC.clearRect(0,0,oC.width,oC.height);  
  13.         oGC.fillRect(step,step,width,height);  
  14.         step++;  
  15.         setInterval("redraw()",30);  
  16.     }  
  17.   
  18.     setInterval("redraw()",30);  
  19. }  
发现会报同样的错误:Uncaught ReferenceError: redraw is not defined。
说到这里也就明了了,因为无论是setInterval还是setTimeout都只能调用全局函数,即挂在window下的函数。
那么解决方案是什么呢?
既然发现了问题是无论setInterval还是setTimeout都只能调用全局函数,
那么只要把redraw函数放到window.onload方法外面定义就可以解决这个棘手问题。还拿setTimeout为例:
  1. window.onload = function(){  
  2.     var oC = document.getElementById('cv'),  
  3.         oGC = oC.getContext('2d'),  
  4.         width = 100,  
  5.         height = 100,  
  6.         step = 0;  
  7.   
  8.     oGC.fillStyle = 'rgba(0,255,255,1)';  
  9.     oGC.fillRect(0,0,width,height);  
  10.   
  11.     setTimeout("redraw()",30);  
  12. }  
  13. function redraw(){  
  14.     oGC.clearRect(0,0,oC.width,oC.height);  
  15.     oGC.fillRect(step,step,width,height);  
  16.     step++;  
  17.     setTimeout("redraw()",30);  
  18. }  
注意一:调用函数时如果函数名外加了引号,则()也要写上,如setTimeout('redraw()',30),不然虽然不会报错,但程序也不会执行。
可以不加引号,只写函数名,如setTimeout(redraw,30),或则函数名(),如setTimeout(redraw(),30)。
注意二:调用函数传递参数时,尽可能使用不带引号的方式传递,因为在加引号的情况下传递参数会出现各种问题,这里就不一一介绍了。
  1. window.onload = function(){  
  2.     var oC = document.getElementById('cv'),  
  3.         oGC = oC.getContext('2d'),  
  4.         width = 100,  
  5.         height = 100,  
  6.         step = 0,  
  7.         param = {  
  8.             oC:oC,  
  9.             oGC:oGC,  
  10.             step:step,  
  11.             width:width,  
  12.             height:height  
  13.         };  
  14.   
  15.     oGC.fillStyle = 'rgba(0,255,255,1)';  
  16.     oGC.fillRect(0,0,width,height);  
  17.   
  18.     setTimeout(redraw(param),30);  
  19. }  
  20.   
  21. function redraw(param){  
  22.     var oC = param.oC,  
  23.         oGC = param.oGC,  
  24.         step = param.step,  
  25.         width = param.width,  
  26.         height = param.height;  
  27.   
  28.     oGC.clearRect(0,0,oC.width,oC.height);  
  29.     oGC.fillRect(step,step,width,height);  
  30.     step++;  
  31.     setTimeout(redraw(param),30);  
  32. }  
还没完,运行了代码发现又有错误:Uncaught RangeError: Maximum call stack size exceeded.
翻译过来就是因为递归次数太多导致内存被耗费太多。真是坑爹啊。
最后在网上找的一个方法_setTimeout替代setTimeout方法,具体原理就不解释了,其实我也不太懂。☺
  1. function _setTimeout(callback,timeout,param){  
  2.     var args = Array.prototype.slice.call(arguments,2);  
  3.     var _cb = function(){  
  4.         callback.apply(null,args);  
  5.     }    
  6.     setTimeout(_cb,timeout);  
  7. }  
完整无误的JS代码:
  1. window.onload = function(){  
  2.     var oC = document.getElementById('cv'),  
  3.         oGC = oC.getContext('2d'),  
  4.         width = 100,  
  5.         height = 100,  
  6.         param = {  
  7.             oC:oC,  
  8.             oGC:oGC,  
  9.             step:step,  
  10.             width:width,  
  11.             height:height  
  12.         };  
  13.     oGC.fillStyle = 'rgba(0,255,255,1)';  
  14.     oGC.fillRect(0,0,width,height);  
  15.     _setTimeout(redraw,30,param);  
  16. }  
  17.   
  18. var step = 0;  
  19.   
  20. function redraw(param){  
  21.     var oC = param.oC,  
  22.         oGC = param.oGC,  
  23.         width = param.width,  
  24.         height = param.height;  
  25.     step++;  
  26.     oGC.clearRect(0,0,oC.width,oC.height);  
  27.     oGC.fillRect(step,step,width,height);  
  28.     _setTimeout(redraw,30,param);  
  29. }  
  30.   
  31. function _setTimeout(callback,timeout,param){  
  32.     var args = Array.prototype.slice.call(arguments,2);  
  33.     var _cb = function(){  
  34.         callback.apply(null,args);  
  35.     }    
  36.     setTimeout(_cb,timeout);  
  37. }  

具体效果请运行下面完整代码:

<!DOCTYPE html>
<html>
<head>
	<meta http_equiv="Content_Type" content="text/html; charset=utf-8" />
	<title>canvas</title>
	<style type="text/css">
		#cv { background-color:#000; margin-left:420px; margin-top:100px; }
	</style>
</head>
<body>
	<canvas id="cv" width="400" height="400">
		<p>该浏览器不支持canvas。</p>
	</canvas> <!-- canvas画布默认宽300px,高150px,背景颜色为白色 -->
<script type="text/javascript">
	window.onload = function(){
		var oC = document.getElementById('cv'),
			oGC = oC.getContext('2d'),
			width = 100,
			height = 100,
			param = {
				oC:oC,
				oGC:oGC,
				step:step,
				width:width,
				height:height
			};
		oGC.fillStyle = 'rgba(0,255,255,1)';
		oGC.fillRect(0,0,width,height);
		_setTimeout(redraw,30,param);
	}
	var step = 0;
	function redraw(param){
		var oC = param.oC,
			oGC = param.oGC,
			width = param.width,
			height = param.height;
		step++;
		oGC.clearRect(0,0,oC.width,oC.height);
		oGC.fillRect(step,step,width,height);
		_setTimeout(redraw,30,param);
	}
	function _setTimeout(callback,timeout,param){
	    var args = Array.prototype.slice.call(arguments,2);
	    var _cb = function(){
	        callback.apply(null,args);
	    }  
	    setTimeout(_cb,timeout);
	}
</script>
</body>
</html>

源引:http://www.58lou.com/separticle.php?artid=202

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: setInterval的缺点是它会无视代码的错误。即使遇到错误,它仍然会一直循环下去,不会停止。这可能导致你的代码中存在一些问题被隐藏起来,很难发现。比如,如果你的代码有一个一定概率会发生错误的部分,并且你使用setInterval来循环调用它,由于setInterval不会因为报错而停止,这个问题可能会被掩盖。\[1\]为了解决这个问题,可以使用setTimeout代替setInterval。可以给setTimeout设置一个间后,在最后调用自身,以实现循环效果。如果希望“匀速”触发,可以计算代码执行间,用希望的延迟减去上次执行的间。\[2\]此外,还需要注意的是,setInterval的第一次执行可能会被略过,导致第一次的调用间与设定的间隔间有较大的差距。\[3\] #### 引用[.reference_title] - *1* *3* [你可能不知道的setInterval的坑](https://blog.csdn.net/u011927449/article/details/106139839)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [setInterval的弊端和解决方案](https://blog.csdn.net/runOnWay/article/details/80900437)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值