如何使用定时器settimeout,setInterval执行能传递参数的函数
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用定时器settimeout,setInterval执行能传递参数的函数相关的知识,希望对你有一定的参考价值。
无论是window.setTimeout还是window.setInterval,在使用函数名作为调用句柄时都不能带参数,而在许多场合必须要带参数,这就需要想方法解决。经网上查询后整理如下:\x0d\x0a例如对于函数hello(_name),它用于针对用户名显示欢\x0d\x0a迎信息:\x0d\x0avar userName="jack";\x0d\x0a//根据用户名显示欢迎信息\x0d\x0afunction hello(_name)\x0d\x0a alert("hello,"+_name);\x0d\x0a\x0d\x0a这时,如果企图使用以下语句来使hello函数延迟3秒执行是不可行的:\x0d\x0awindow.setTimeout(hello(userName),3000);\x0d\x0a这将使hello函数立即执行,并将返回值作为调用句柄传递给setTimeout函数,其结果并不是程序需要的。而使用字符串形式可以达到想要的结果:\x0d\x0awindow.setTimeout("hello(userName)",3000);这是方法(一)\x0d\x0a这里的字符串是一段javascript代码,其中的userName表示的是变量,而且经测试,这个变量要是个全局的,如果是在某函数里面如此调用 setTimeout,而此变量只是个函数内部变量的话,是会报变量不存在的。但这种写法不够直观,而且有些场合必须使用函数名,于是有人想到了如下\x0d\x0a方法(二):\x0d\x0a 参考技术A 例如对于函数hello(_name),它用于针对用户名显示欢迎信息:
var userName="jack";
//根据用户名显示欢迎信息
function hello(_name)
alert("hello,"+_name);
这时,如果企图使用以下语句来使hello函数延迟3秒执行是不可行的:
window.setTimeout(hello(userName),3000);
这将使hello函数立即执行,并将返回值作为调用句柄传递给setTimeout函数,其结果并不是程序需要的。而使用字符串形式可以达到想要的结果:
window.setTimeout("hello(userName)",3000);这是方法(一)
这里的字符串是一段JavaScript代码,其中的userName表示的是变量,而且经测试,这个变量要是个全局的,如果是在某函数里面如此调用 setTimeout,而此变量只是个函数内部变量的话,是会报变量不存在的。但这种写法不够直观,而且有些场合必须使用函数名,于是有人想到了如下
方法(二):
<script language="JavaScript" type="text/javascript">
<!--
var userName="jack";
//根据用户名显示欢迎信息
function hello(_name)
alert("hello,"+_name);
//创建一个函数,用于返回一个无参数函数
function _hello(_name)
return function()
hello(_name);
window.setTimeout(_hello(userName),3000);
//-->
</script>
这 里定义了一个函数_hello,用于接收一个参数,并返回一个不带参数的函数,在这个函数内部使用了外部函数的参数,从而对其调用,不需要使用参数。在 window.setTimeout函数中,使用_hello(userName)来返回一个不带参数的函数句柄,从而实现了参数传递的功能。
另外也有人通过修改settimeout、setInterval来实现,相比是比较理想的。即下面的
方法三:
<script language="JavaScript" type="text/javascript">
<!--
var userName="jack";
//根据用户名显示欢迎信息
function hello(_name)
alert("hello,"+_name);
//*=============================================================
//* 功能: 修改 window.setInterval ,使之可以传递参数和对象参数
//* 方法: setInterval (回调函数,时间,参数1,,参数n) 参数可为对象:如数组等
//*=============================================================
var __sto = setInterval;
window.setInterval = function(callback,timeout,param)
var args = Array.prototype.slice.call(arguments,2);
var _cb = function()
callback.apply(null,args);
__sto(_cb,timeout);
window.setInterval(hello,3000,userName);
//-->本回答被提问者采纳
为啥 requestAnimationFrame 比 setInterval 或 setTimeout 更好
【中文标题】为啥 requestAnimationFrame 比 setInterval 或 setTimeout 更好【英文标题】:Why is requestAnimationFrame better than setInterval or setTimeout为什么 requestAnimationFrame 比 setInterval 或 setTimeout 更好 【发布时间】:2016-12-07 04:30:15 【问题描述】:为什么我应该使用 requestAnimationFrame 而不是 setTimeout 或 setInterval?
这个自我回答的问题是一个文档示例。
【问题讨论】:
您也可以查看this 【参考方案1】:高质量的动画。
这个问题是最简单的回答。 requestAnimationFrame
产生更高质量的动画,完全消除使用 setTimeout
或 setInterval
时可能发生的闪烁和剪切,并减少或完全消除跳帧。
是当一个新的画布缓冲区在显示扫描中途呈现给显示缓冲区时,导致动画位置不匹配导致剪切线。
闪烁是在画布完全渲染之前将画布缓冲区呈现给显示缓冲区时引起的。
跳帧是在渲染帧之间的时间与显示硬件不精确同步时引起的。每隔这么多帧就会跳过一帧,从而产生不一致的动画。 (有一些方法可以减少这种情况,但我个人认为这些方法会产生更差的整体结果)由于大多数设备每秒使用 60 帧(或数倍),导致每 16.666...ms 和计时器 setTimeout
和 @ 987654327@ 使用整数值,它们永远无法完美匹配帧速率(如果您有interval = 1000/60
,则四舍五入到 17 毫秒)
一个演示值一千字。
更新问题requestAnimationFrame loop not correct fps的答案显示了setTimeout的帧时间如何不一致,并与requestAnimationFrame进行比较。
演示展示了一个简单的动画(条纹在屏幕上移动),单击鼠标按钮将在使用的渲染更新方法之间切换。
使用了多种更新方法。这将取决于您正在运行的硬件设置,以了解动画工件的确切外观。你会在条纹的运动中寻找微小的抽搐
Timer 使用 setTimeout 设置动画。时间是 1000/60 RAF 最佳质量,使用 requestAnimationFrame 制作动画注意。您可能关闭了显示同步,或关闭了硬件加速,这将影响所有计时方法的质量。低端设备也可能有动画问题
双计时器,使用两个计时器,一个每 1000/60 清除调用一次,另一个用于渲染。
2019 年 10 月更新 计时器呈现内容的方式发生了一些变化。为了显示setInterval
不能正确地与显示刷新同步,我更改了双计时器示例,以显示使用多个setInterval
仍会导致严重闪烁。这将产生的闪烁程度取决于硬件设置。
带有定时动画的 RAF,使用 requestAnimationFrame,但使用帧经过时间制作动画。这种技术在动画中很常见。我认为这是有缺陷的,但我将其留给观众
带有定时动画的计时器。作为“带有定时动画的 RAF”,在这种情况下用于克服“定时器”方法中出现的跳帧。我再次认为这很糟糕,但游戏社区发誓这是当您无法访问显示刷新时使用的最佳方法/** SimpleFullCanvasMouse.js begin **/
var backBuff;
var bctx;
const STRIPE_WIDTH = 250;
var textWidth;
const helpText = "Click mouse to change render update method.";
var onResize = function()
if(backBuff === undefined)
backBuff = document.createElement("canvas") ;
bctx = backBuff.getContext("2d");
backBuff.width = canvas.width;
backBuff.height = canvas.height;
bctx.fillStyle = "White"
bctx.fillRect(0,0,w,h);
bctx.fillStyle = "Black";
for(var i = 0; i < w; i += STRIPE_WIDTH)
bctx.fillRect(i,0,STRIPE_WIDTH/2,h) ;
ctx.font = "20px arial";
ctx.textAlign = "center";
ctx.font = "20px arial";
textWidth = ctx.measureText(helpText).width;
;
var tick = 0;
var displayMethod = 0;
var methods = "Timer,RAF Best Quality,Dual Timers,RAF with timed animation,Timer with timed animation".split(",");
var dualTimersActive = false;
var hdl1, hdl2
function display(timeAdvance) // put code in here
tick += timeAdvance;
tick %= w;
ctx.drawImage(backBuff,tick-w,0);
ctx.drawImage(backBuff,tick,0);
if(textWidth !== undefined)
ctx.fillStyle = "rgba(255,255,255,0.7)";
ctx.fillRect(w /2 - textWidth/2, 0,textWidth,40);
ctx.fillStyle = "black";
ctx.fillText(helpText,w/2, 14);
ctx.fillText("Display method : " + methods[displayMethod],w/2, 34);
if(mouse.buttonRaw&1)
displayMethod += 1;
displayMethod %= methods.length;
mouse.buttonRaw = 0;
lastTime = null;
tick = 0;
if(dualTimersActive)
dualTimersActive = false;
clearInterval(hdl1);
clearInterval(hdl2);
updateMethods[displayMethod]()
//==================================================================================================
// The following code is support code that provides me with a standard interface to various forums.
// It provides a mouse interface, a full screen canvas, and some global often used variable
// like canvas, ctx, mouse, w, h (width and height), globalTime
// This code is not intended to be part of the answer unless specified and has been formated to reduce
// display size. It should not be used as an example of how to write a canvas interface.
// By Blindman67
const U = undefined;const RESIZE_DEBOUNCE_TIME = 100;
var w,h,cw,ch,canvas,ctx,mouse,createCanvas,resizeCanvas,setGlobals,globalTime=0,resizeCount = 0;
var L = typeof log === "function" ? log : function(d) console.log(d);
createCanvas = function () var c,cs; cs = (c = document.createElement("canvas")).style; cs.position = "absolute"; cs.top = cs.left = "0px"; cs.zIndex = 1000; document.body.appendChild(c); return c;
resizeCanvas = function ()
if (canvas === U) canvas = createCanvas(); canvas.width = window.innerWidth; canvas.height = window.innerHeight; ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") setGlobals(); if (typeof onResize === "function") resizeCount += 1; setTimeout(debounceResize,RESIZE_DEBOUNCE_TIME);
function debounceResize() resizeCount -= 1; if(resizeCount <= 0) onResize();
setGlobals = function() cw = (w = canvas.width) / 2; ch = (h = canvas.height) / 2; mouse.updateBounds();
mouse = (function()
function preventDefault(e) e.preventDefault();
var mouse =
x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false, buttonRaw : 0, over : false, bm : [1, 2, 4, 6, 5, 3],
active : false,bounds : null, crashRecover : null, mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
;
var m = mouse;
function mouseMove(e)
var t = e.type;
m.x = e.clientX - m.bounds.left; m.y = e.clientY - m.bounds.top;
m.alt = e.altKey; m.shift = e.shiftKey; m.ctrl = e.ctrlKey;
if (t === "mousedown") m.buttonRaw |= m.bm[e.which-1];
else if (t === "mouseup") m.buttonRaw &= m.bm[e.which + 2];
else if (t === "mouseout") m.buttonRaw = 0; m.over = false;
else if (t === "mouseover") m.over = true;
else if (t === "mousewheel") m.w = e.wheelDelta;
else if (t === "DOMMouseScroll") m.w = -e.detail;
if (m.callbacks) m.callbacks.forEach(c => c(e));
if((m.buttonRaw & 2) && m.crashRecover !== null) if(typeof m.crashRecover === "function") setTimeout(m.crashRecover,0);
e.preventDefault();
m.updateBounds = function()
if(m.active)
m.bounds = m.element.getBoundingClientRect();
m.addCallback = function (callback)
if (typeof callback === "function")
if (m.callbacks === U) m.callbacks = [callback];
else m.callbacks.push(callback);
else throw new TypeError("mouse.addCallback argument must be a function");
m.start = function (element, blockContextMenu)
if (m.element !== U) m.removeMouse();
m.element = element === U ? document : element;
m.blockContextMenu = blockContextMenu === U ? false : blockContextMenu;
m.mouseEvents.forEach( n => m.element.addEventListener(n, mouseMove); );
if (m.blockContextMenu === true) m.element.addEventListener("contextmenu", preventDefault, false);
m.active = true;
m.updateBounds();
m.remove = function ()
if (m.element !== U)
m.mouseEvents.forEach(n => m.element.removeEventListener(n, mouseMove); );
if (m.contextMenuBlocked === true) m.element.removeEventListener("contextmenu", preventDefault);
m.element = m.callbacks = m.contextMenuBlocked = U;
m.active = false;
return mouse;
)();
resizeCanvas();
mouse.start(canvas,true);
onResize()
var lastTime = null;
window.addEventListener("resize",resizeCanvas);
function clearCTX()
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.clearRect(0,0,w,h); // though not needed this is here to be fair across methods and demonstrat flicker
function dualUpdate()
if(!dualTimersActive)
dualTimersActive = true;
hdl1 = setInterval( clearCTX, 1000/60);
hdl2 = setInterval(() => display(10), 1000/60);
function timerUpdate()
timer = performance.now();
if(!lastTime)
lastTime = timer;
var time = (timer-lastTime) / (1000/60);
lastTime = timer;
setTimeout(updateMethods[displayMethod],1000/60);
clearCTX();
display(10*time);
function updateRAF()
clearCTX();
requestAnimationFrame(updateMethods[displayMethod]);
display(10);
function updateRAFTimer(timer) // Main update loop
clearCTX();
requestAnimationFrame(updateMethods[displayMethod]);
if(!timer)
timer = 0;
if(!lastTime)
lastTime = timer;
var time = (timer-lastTime) / (1000/60);
display(10 * time);
lastTime = timer;
displayMethod = 1;
var updateMethods = [timerUpdate,updateRAF,dualUpdate,updateRAFTimer,timerUpdate]
updateMethods[displayMethod]();
/** SimpleFullCanvasMouse.js end **/
【讨论】:
好的答案,一个注意事项是浏览器会自动为你做双缓冲,所以普通的画布永远不会有剪切的风险,闪烁也是如此。 @Cristy 是的 DOM 双缓冲区。但是,退出时的函数(包括超时和间隔)(执行空闲又名调用堆栈为空)已渲染后缓冲区立即呈现给显示 RAM,这可能是中间扫描如果您使用单个函数渲染这将导致动画剪切,并且闪烁如果使用两个函数进行渲染,这两个函数都退出到空闲状态。requestAnimationFrames
s 回调是特殊的,退出时后台缓冲区会一直保持到垂直同步(没有像素移动到显示)这会停止剪切和闪烁。
我在所有示例中都看到了纯粹的东西。这些线条看起来像楼梯。动画很流畅,但似乎没有任何跳帧。有没有办法在不降低帧率的情况下纠正这个问题? (Chrome v84,糟糕的老联想,显卡更差。)
@VictorStoddard 检查显卡设备设置。确保它没有设置为覆盖垂直同步。
@Blindman67 如果你还记得的话,你在 20 天前在 Code Review Stack Exchange 上回答了我的问题:codereview.stackexchange.com/questions/252922/…我再也找不到你的答案了。你能重新回答一下吗,因为它解释得很漂亮:)以上是关于如何使用定时器settimeout,setInterval执行能传递参数的函数的主要内容,如果未能解决你的问题,请参考以下文章
如何使用定时器settimeout,setInterval执行能传递参数的函数
如何使用定时器settimeout,setInterval执行能传递参数的函数
如何使用定时器settimeout,setInterval执行能传递参数的函数