美文网首页
setTimeout和for循环引发的常识错误

setTimeout和for循环引发的常识错误

作者: physihan | 来源:发表于2017-04-14 21:22 被阅读0次

    标签(空格分隔): 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还是setIntervalstackoverflow上有解释,基本上是可以互相替换的,本人更加推荐使用setInterval,认为它更适合实现间隔的功能。
    至此,已经解决了 for 循环的 bug,总结原因,还是自己对这个函数有点想当然,然而真正去写的时候才能发现问题,可能也有一些朋友会犯和我一样的错误,这里写出来和大家分享一下。

    相关文章

      网友评论

          本文标题:setTimeout和for循环引发的常识错误

          本文链接:https://www.haomeiwen.com/subject/gfygattx.html