JavaScript函数和UI更新

JavaScript函数和UI更新,javascript,settimeout,Javascript,Settimeout,我有一个下面的函数,它将相对定位的元素1000px从现在的位置滑出 for (var i = 0; i < 1000; i++) { $('.my-element').css( 'left', parseInt($('.my-element').css('left'), 10) + 1 ); } for(变量i=0;i

我有一个下面的函数,它将相对定位的元素1000px从现在的位置滑出

for (var i = 0; i < 1000; i++) {
    $('.my-element').css(
        'left',
        parseInt($('.my-element').css('left'), 10) + 1
    );
}
for(变量i=0;i<1000;i++){
$('.my元素').css(
"左",,
parseInt($('.my element').css('left'),10)+1
);
}
这不会产生滑动效果。相反,在执行结束时,元素突然向右移动1000px

现在,如果我将UI更新包装在setTimeout中,如下所示:

for (var i = 0; i < 1000; i++) {
    setTimeout(function () {
        $('.my-element').css(
            'left',
            parseInt($('.my-element').css('left'), 10) + 1
        );
    }, 0);
}
for(变量i=0;i<1000;i++){
setTimeout(函数(){
$('.my元素').css(
"左",,
parseInt($('.my element').css('left'),10)+1
);
}, 0);
}
这会产生元素向右滑动1000px的视觉效果

现在,根据我的理解和这个SO线程,UI更新在浏览器事件队列中排队,就像异步回调像setTimeout回调一样排队一样

因此,在第一种情况下,基本上,在执行for循环时,会生成一个包含1000个UI更新的队列

在第二种情况下,首先创建一个包含1000个setTimeout回调的队列,该队列在执行时创建另一个包含1000个UI更新的队列

因此,最终,这两种情况都会创建相同的1000个UI更新队列。那么为什么视觉效果会有差异呢

我必须在这里查看一些重要的JavaScipt和浏览器渲染概念。任何能启发我的人都将不胜感激


注意:以上示例纯粹是为了理解目的,而不是试图创建一个JS函数来滑动DOM元素。

这可能是最好的思考方法。浏览器可以做两件事之一。它要么运行javascript,要么呈现webapge,两者都不能

这是因为javascript代码是100%阻塞的,这意味着在浏览器执行所有阻塞代码之前,它永远不会放弃控制

第一个示例只包含块代码,因此浏览器永远不会有机会渲染,直到元素已经到达需要的位置

您的第二个示例包含使用setTimeout(延迟阻塞代码)的阻塞代码,该代码将一组阻塞代码排队,以便稍后(在所有其他阻塞代码完成后)由浏览器自行决定(在其呈现和javascript运行周期之间)执行

因此,第二个例子是循环将完全执行,将1000个函数排队在某个时间点执行,但尽可能接近0毫秒。现在阻塞代码已经完成了一个或多个setTimeout可以执行,或者浏览器可以渲染,但是实际发生的事情是非常随机的。但它会在呈现和执行javascript之间来回穿梭

以这段代码为例

setTimeout(function () { //this makes it so the page loads and sits for a second
    var delay = 100, //delay between animations
        distance = 25, //total distance moved
        sync = false; //should this use blocking code

    if (sync) {
        var i = 0,
            elapsed = 0,
            last = new Date();
        while (i < distance) {
            var now = new Date();
            elapsed += (now - last);
            last = now;
            if (elapsed >= delay) {
                move(i++);
                elapsed -= delay;
            }
        }
    } else {
        for (var i = 0; i < distance; i++) {
            assyncMove(i, delay * i);
        }
    }

    function assyncMove(position, delay) {
        setTimeout(function () {
            move(position);
        }, delay);
    }

    function move(position) {
        $("div").css("left", position);
    }
}, 1000);
setTimeout(函数(){//这使得页面加载并放置一秒钟
var delay=100,//动画之间的延迟
距离=25,//移动的总距离
sync=false;//应该使用阻塞代码吗
如果(同步){
var i=0,
时间=0,
last=新日期();
while(i=延迟){
move(i++);
已用-=延迟;
}
}
}否则{
对于(变量i=0;i
您可以更改
延迟
距离
同步
变量。两个循环都等待在每个动画之间移动元素
delay
毫秒。它们都将把一个div总共移动
距离
像素。但是,其中一个(setTimeout)将有一个可见的动画,而另一个将只是拍摄。如果您使同步方法的延迟或距离过长,则实际上会冻结浏览器,同步解决方案将不会有此问题


Logan肯定回答了您的问题,但我建议您研究一下滑动效果,而不是循环。试想一下,for循环的每一次迭代都在穿越DOM,寻找一类
的所有元素。my element
-几乎没有效率!此示例纯粹是为了理解浏览器如何执行JavaScript和更新UI。不是尝试创建滑动功能。但是,如果UI更新排队到浏览器事件队列,则第一个示例将添加到队列1000 UI更新,其中浏览器必须为每个队列项目在不同位置绘制元素。(因此,我认为会产生滑动视觉效果)在第二个示例中,1000个排队的setTimeout回调必须在setTimeout回调结束时创建1000个UI更新,我认为这与第一个示例相同。@Prashant第一个示例中似乎没有队列。脚本将元素的左CSS属性更改1000次。当浏览器开始呈现页面时,它只看到最终结果。仅仅更改CSS属性本身不会触发重画。好的,这意味着链接线程上的这个注释是误导性的。因为在没有setTimeout的示例中,他提到将UI更新添加到队列中。他甚至还提到,在JS函数结束时,元素的重新绘制速度非常快,即使发生了三次不同的UI更新,似乎只有最后一次更新是可见的。因此,我添加了代码,显示无论延迟如何,由于执行阻塞javascript代码,您在渲染时总会遇到延迟。您可以遵循JSFIDLE链接并处理变量