JavaScript函数和UI更新

Pra*_*ant 3 javascript settimeout

我有一个跟随功能,它将相对定位的元素1000px从它现在的位置滑下.

for (var i = 0; i < 1000; i++) {
    $('.my-element').css(
        'left',
        parseInt($('.my-element').css('left'), 10) + 1
    );
}
Run Code Online (Sandbox Code Playgroud)

这不会产生滑动效果.相反,在执行结束时,元素突然向右移动1000px.

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

for (var i = 0; i < 1000; i++) {
    setTimeout(function () {
        $('.my-element').css(
            'left',
            parseInt($('.my-element').css('left'), 10) + 1
        );
    }, 0);
}
Run Code Online (Sandbox Code Playgroud)

这会产生元素向右滑动1000px的视觉效果.

现在,根据我的不足和这个SO线程,为什么setTimeout(fn,0)有时会有用?,UI更新在浏览器事件队列中排队,就像同步回调像setTimeout回调一样排队.

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

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

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

我必须在这里查看一些重要的JavaScipt和浏览器呈现概念.任何能够启发我的人都会非常感激.

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

Log*_*phy 6

这可能是考虑它的最佳方式.浏览器可以执行以下两种操作之一.要么运行你的javascript,要么渲染webapge,它不能同时执行这两个操作.

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

您的第一个示例仅包含阻塞代码,因此在元素已经存在之前,浏览器永远不会有机会进行渲染.

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

所以循环的第二个例子将完全执行,排队1000个函数在某个时间点执行,但尽可能接近0ms.现在阻塞代码已经完成了一个或多个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);
Run Code Online (Sandbox Code Playgroud)

你可以改变的delay,distancesync变量.两个循环都等待delay在每个动画之间移动元素毫秒.它们都会移动一个div总共distance像素.然而,一个(setTimeout)将具有可见动画而另一个将刚刚拍摄.如果您使同步方法的延迟或距离太长,您实际上将冻结浏览器,则assync解决方案将不会出现此问题!

http://jsfiddle.net/j79s4o4w/3/