javascript 如果可能的话,我真的不应该使用 setInterval 和 setTimeout 吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30722677/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it true that if possible I should never use setInterval & setTimeout?
提问by irenicus
I am learning to code in JavaScript. I am programming something with some timed mouse animation. I'm just about to add some code which draws the mouse path.
我正在学习用 JavaScript 编写代码。我正在用一些定时鼠标动画来编程。我正要添加一些绘制鼠标路径的代码。
It's going to be something that takes the mousemove event, and every time the mouse moves draw a new line path on Canvas. And with time, that path is going to be more transparent until it disappears. Of course, new paths are always going to be opaque so there's a continuous movement.
它将使用 mousemove 事件,每次鼠标移动时都会在 Canvas 上绘制一条新的线路径。随着时间的推移,这条路径将变得更加透明,直到它消失。当然,新路径总是不透明的,所以会有持续的运动。
I figured out a way I can do this with just requestanimationframe. Basically every time a new mousemove event occurs, I will add that mouse path coordinates to an array of objects called mousePathArray. The object will carry the path coordinates and an "animationStage" counter. This counter will basically determine how transparent the path will be at that stage of the 'animation'. (Later stages will mean more transparent.)
我想出了一种方法,我可以只用 requestanimationframe 来做到这一点。基本上每次发生新的 mousemove 事件时,我都会将该鼠标路径坐标添加到名为 mousePathArray 的对象数组中。该对象将携带路径坐标和“animationStage”计数器。该计数器将基本上确定路径在“动画”阶段的透明度。(后期将意味着更加透明。)
Then every animation frame I will call a function that will go through all the objects in the array and draw the lines according to their coordinates and animationStage counter, will increase the counter by 1 and will remove the array objects if the animationStage counter reaches the end number (which could be 50 or something).
然后每个动画帧我将调用一个函数,该函数将遍历数组中的所有对象并根据它们的坐标和 animationStage 计数器绘制线条,将计数器增加 1 并在 animationStage 计数器到达末尾时删除数组对象数字(可能是 50 或其他)。
This can all be done, but it sounds like instead of all of that, it would be much easier to do by just introducing a setInterval function that will be called with a set interval every time the mouse moves.
这一切都可以完成,但听起来比所有这些都更容易,只需引入一个 setInterval 函数,该函数将在每次鼠标移动时以设定的间隔调用。
So is it worth it doing the long way? Would it be faster or maybe just be better JS practice to not use setInterval and rAF together?
那么,长途跋涉值得吗?不一起使用 setInterval 和 rAF 会更快还是更好的 JS 实践?
After writing all this down I actually wrote the rAF-only code that I talked about above. It's too long to paste here but the rules need it. Here it is on jsfiddle: http://jsfiddle.net/06f7zefn/2/
在写下所有这些之后,我实际上编写了我上面谈到的 rAF-only 代码。粘贴在这里太长了,但规则需要它。这是在 jsfiddle 上:http: //jsfiddle.net/06f7zefn/2/
(I'm aware there are a lot of inefficiencies and probably terrible coding practice but bear with me, I'm 5 days old in this! I could make an isDrawing? boolean instead of calling animate() on every frame, and I can just do ctx.moveTo() once with the rest being LineTo() without having to to moveTo() every iteration since one point originates from where the other left off)
(我知道有很多低效率和糟糕的编码实践,但请耐心等待,我已经 5 天了!我可以制作一个 isDrawing? boolean 而不是在每一帧上调用 animate(),我可以只需执行 ctx.moveTo() 一次,其余为 LineTo() 而不必每次迭代都执行 moveTo(),因为一个点源自另一个停止的位置)
If I could get across the main idea I'm talking about, that is where I am asking for your opinion. Instead of making everything related to timing originate from the rAF call could it be better to use setInterval or setTimeout here instead?
如果我能理解我正在谈论的主要想法,那就是我征求你的意见的地方。与其让与计时相关的一切都源自 rAF 调用,不如在这里使用 setInterval 或 setTimeout 更好吗?
var canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
var currentPosX, currentPosY, prevPosX, prevPosY;
var mousePathArray = [];
canvas.addEventListener ('mousemove', mouseOp);
function mouseOp (mouseEvent) {
prevPosX = currentPosX;
prevPosY = currentPosY;
currentPosX = mouseEvent.clientX;
currentPosY = mouseEvent.clientY;
mousePathArray.push( {
x1: currentPosX,
x2: prevPosX,
y1: currentPosY,
y2: prevPosY,
animStage: 0
});
}
function animate () {
var anims = mousePathArray.length;
if (anims!=0) {
for (i=0; i<anims; i++) {
if (mousePathArray[i].animStage == 20) {
mousePathArray.splice(i, 1);
i--;
anims--;
continue;
}
drawLine(mousePathArray[i].x1, mousePathArray[i].x2, mousePathArray[i].y1, mousePathArray[i].y2, 1 - (mousePathArray[i].animStage * 0.05));
mousePathArray[i].animStage ++;
}
}
}
function drawLine (x1, x2, y1, y2, alpha) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.strokeStyle = "rgba(150, 20, 150," + alpha +")";
ctx.stroke();
}
animloop();
function animloop(){
window.requestAnimationFrame(animloop);
gameLoop();
}
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
animate();
}
回答by Iman Mohamadi
I don't think using setInterval or setTimeout is bad practice. Using setTimeout is bad practice when you want to do something in future but you don't exactly know when you will be able to do that. For example this is bad practice:
我不认为使用 setInterval 或 setTimeout 是不好的做法。当您想在将来做某事但您不确切知道何时能够做到时,使用 setTimeout 是一种不好的做法。例如,这是不好的做法:
makeHeavyDomMovements();
setTimeout(function () {
//with 3000 timeout I'm sure any device has made my changes
makeNextMove();
}, 3000);
the correct way was:
正确的方法是:
makeHeavyDomMovements().
then(function () {
makeNextMove();
});
If you want to do something in future like respond to a user action 100ms later, it's best practice to use setTimeout or if you want to put something in browser queue, you should use setTimeout (or use a worker if needed).
如果你想在未来做一些事情,比如在 100 毫秒后响应用户操作,最好使用 setTimeout 或者如果你想把一些东西放在浏览器队列中,你应该使用 setTimeout (或者在需要时使用一个工作程序)。
It's the same as setInterval, if you are using to do something that should be done every x milliseconds, well you are using it correctly and it's not bad practice, here is a bad usage of setInterval:
它与 setInterval 相同,如果您习惯于做一些应该每 x 毫秒完成一次的事情,那么您正在正确使用它并且这不是坏习惯,这是 setInterval 的错误用法:
var dbInterval = setInterval(function () {
if (dbIsReady()) {
clearInterval(dbInterval);
fireReadyEvent();
}
}, 300);
And here is a regular usage of setInterval:
这是 setInterval 的常规用法:
setInterval(function () {
runSync();
}, 600000);
Bad practices and good practices are defied by the way you use your environment tools not those tools themselves.
不好的做法和好的做法取决于您使用环境工具的方式,而不是这些工具本身。
回答by markE
Luz Caballero does a fair job of describing why rAF is a useful replacement for setInterval
:
Luz Caballero 很好地描述了为什么 rAF 是一个有用的替代品setInterval
:
https://dev.opera.com/articles/better-performance-with-requestanimationframe/.
https://dev.opera.com/articles/better-performance-with-requestanimationframe/。
As for me, I now use rAF instead of setInterval because rAF has some built-in usefulness that would require additional coding with setInterval:
至于我,我现在使用 rAF 而不是 setInterval,因为 rAF 有一些内置的用处,需要使用 setInterval 进行额外编码:
rAF will attempt to synchronize its calls with the display refresh cycle. This gives the code in the loop the "best chance" to complete between the refresh cycles.
If the tasks in loop#1 can't complete before loop#2 is requested, then the new loop#2 won't be called until the incomplete loop#1 is completed. This means the browser won't be overworked with a backlog of accumulated loops.
If the user switches to a different browser tab then loops in the current tab are suspended. This allows processing power to shift to the new tab instead of being used in the now invisible loop tap. This is also a power-saving feature on battery powered devices. If you want the loop to continue processing while a tab is unfocused you must use setInterval.
The rAF callback function automatically gets a high-precision timestamp argument. This allows rAF loops to calculate & use elapsed times. This elaped time can be used to (1) delay until a specified elapsed time has occurred or (2) "catch up" on time based tasks that were desired but were unable to be completed.
rAF 将尝试将其调用与显示刷新周期同步。这为循环中的代码提供了在刷新周期之间完成的“最佳机会”。
如果循环#1 中的任务在请求循环#2 之前无法完成,则在未完成的循环#1 完成之前不会调用新的循环#2。这意味着浏览器不会因积压的循环而过度工作。
如果用户切换到不同的浏览器选项卡,则当前选项卡中的循环将暂停。这允许处理能力转移到新选项卡,而不是在现在不可见的循环抽头中使用。这也是电池供电设备的节能功能。如果您希望在选项卡未聚焦时循环继续处理,则必须使用 setInterval。
rAF 回调函数自动获取高精度时间戳参数。这允许 rAF 循环计算和使用经过时间。此经过时间可用于 (1) 延迟到指定的经过时间发生或 (2) “赶上”需要但无法完成的基于时间的任务。
回答by Gauthier Peel
the problem is not with setTimeout() but with setInterval() which could lost some executions and has other drawbacks. So always use setTimeout. You have all the details on : https://zetafleet.com/blog/2010/04/why-i-consider-setinterval-to-be-harmful.html
问题不在于 setTimeout() 而在于 setInterval() 可能会丢失一些执行并具有其他缺点。所以总是使用setTimeout。您拥有以下所有详细信息:https: //zetafleet.com/blog/2010/04/why-i-think-setinterval-to-be-harmful.html
回答by U.Aasd
you can use them if you are 100% sure that it is the exact time you need you. if it is an ambiguous situation then not to use it. if you are using a javascript framework look for a lifecycle hooks e.g in angular use Angular life cycle hooks.
如果您 100% 确定这是您需要的确切时间,则可以使用它们。如果这是一个模棱两可的情况,那么不要使用它。如果您使用的是 javascript 框架,请寻找生命周期钩子,例如在 angular 中使用 Angular 生命周期钩子。