标签(空格分隔): javascript
最近想要去实现一个定时函数,定时的去改变一个元素的opacity
,从而实现淡入浅出的效果,我写出的代码是这样的
var pic=document.getElementById('picture')
for(var i=0;i<=10;i++){
setTimeout(function(){
pic.style.opacity=i*0.1
console.log(i)
},1000)
}
结果一直不变,在控制台console.log(i)
发现i一直没变,每次输出都是11,想了想是因为setTimeout()
函数是异步实现的,在开始回调函数的时候,for循环早已经结束了,而引用的i则是最后一次循环结束后i的值11。
那我就用闭包来保存i的值吧
for(var i=0;i<=10;i++){
(function(j){
setTimeout(function(){
pic.style.opacity=j*0.1
console.log(j)
},1000)
})(i)
}
试了一下,发现还是不行,这次console对了能正确输出i的值,但这不是我想要的啊,我要实现opacity
定时改变,然而这个怎么是一秒就完成了?理想中的结果应该是隔一秒输出一个数啊,我在查阅一些资料以后发现,这已经不是闭包能解决的了,由于javascript的单线程的机制,settimeout
的执行必然是在循环之后的,即使把倒计时时间设为0也是这样。
我又尝试了下不在循环里面写 setTimeout
,结果也是这样
setTimeout(function () {
console.log(1)
}, 3000);
setTimeout(function () {
console.log(2)
}, 3000);
setTimeout(function () {
console.log(3)
}, 3000);
结果是在3秒后连续输出了123,并没有停顿3秒,因为setTimeout
这个倒计时函数的回调是异步执行的,这样for循环的问题就可以解释了,for循环执行了多次setTimeout
函数,但是回调都是各自独立的,都是在1秒后执行回调函数,几乎在同时把我想要的那个元素的opacity设置了11次,自然是看不到“淡入”的效果了。看样子我是把setTimeout
函数当成延时函数是不能这么写的了,那还有没有别的方法呢
方法肯定是有的,可以去尝试一下回调地狱
setTimeout(function(){
console.log(1)
setTimeout(function(){
console.log(2)
setTimeout(function(){
console.log(3)
...
},1000)
},1000)
},1000)
这实际上就是递归调用自身,抽象一下
var makeInterval=function(){
var times=0;
var step=function(){
console.log(times)
times++
if(times<10){
setTimeout(step,1000)
}
}
setTimeout(step,0)
}
makeInterval()
实际上还可以使用setInterval
来写这段代码
var makeInterval=function(){
var times=0;
var timer=setInterval(function(){
if(times==9){
clearTimeout(timer)
}
console.log(times)
times++
},1000)
}
makeInterval()
关于使用setTimeout
还是setInterval
在stackoverflow上有解释,基本上是可以互相替换的,本人更加推荐使用setInterval,认为它更适合实现间隔的功能。
至此,已经解决了 for 循环的 bug,总结原因,还是自己对这个函数有点想当然,然而真正去写的时候才能发现问题,可能也有一些朋友会犯和我一样的错误,这里写出来和大家分享一下。
网友评论