setTimeout和callback - 不允许Browser渲染刷新的DOM元素

时间:2016-03-26 07:38:45

标签: javascript callback settimeout

我有一个更新一些DOM元素的函数,然后使用setTimeout调用回调,如下所示(如果提供了一个 - 否则调用一个虚函数):

        function updateprogressbar(id,val,label,callback) {
            var time = 0;
            if (typeof callback === "function") {
                var func = callback;
            } else {
                var func = function () {};
            }
            $( "#progressbar" ).progressbar({
                value: val
            });
            $( "#progress-label"+id ).html("Overall Progress: "+label);
            setTimeout(func(),time);
        }

使用此for循环调用:

        for(var i=0;i<emails.length;i++) {
            var howmuchdone = Math.ceil(totalchecked*100/totaltocheck);
            updateprogressbar(1,howmuchdone,howmuchdone+"%",function () {                   
                $("#EMAIL_ALL").val($("#EMAIL_ALL").val()+","+getmeadescriptor($.trim(emails[i])));
            });
            totalchecked = totalchecked + 1;
        }

它正在正确执行它的回调,但我使用setTimeout的目的是确保浏览器呈现更新的DOM元素。由于我无法弄清楚的原因,DOM元素没有被更新。

(在for循环中调用updateprogressbar,并且传递给它的回调是一个匿名函数,它执行一些简单的处理 - 尽管我不认为这与此问题相关。)

关于为什么我没有在updateprogressbar调用上获得任何浏览器呈现的任何想法?

谢谢!

所以,按照@ vinhHT的评论,我会试试这个:

                var testfunction = function (value) {
                    $("#EMAIL_ALL").val($("#EMAIL_ALL").val()+","+value;
                }

                for(var i=0;i<emails.length;i++) {
                    var howmuchdone = Math.ceil(totalchecked*100/totaltocheck);
                    updateprogressbar(1,howmuchdone,howmuchdone+"%",testfunction(getmeadescriptor($.trim(emails[i]))));
                    totalchecked = totalchecked + 1;
                }

2 个答案:

答案 0 :(得分:2)

在setTimeout中删除()

As setTimeout(func,time);

在jfriend00的评论之后编辑:

  1. 将函数正确声明为:

    var func = function(){   //生意就在这里 }

  2. 在代码中更正对上述回调的调用

    setTimeout(func,time);

  3. 3编辑问题已被编辑:在if-else块之外声明func,更正setTimeout调用

    function updateprogressbar(id, val, label, callback) {
        var time = 0;
        var func;
        if (typeof callback === "function") {
            func = callback;
        } else {
            func = function() {};
        }
        $("#progressbar").progressbar({
            value: val
        });
        $("#progress-label" + id).html("Overall Progress: " + label);
        setTimeout(func, time);
    }
    

答案 1 :(得分:0)

修改

现在,您已经公开了for循环代码,我们可以看到,为了让循环的每个循环在下一个循环执行之前重新绘制,您需要开始循环的每个下一次迭代一个setTimeout()。这允许代码返回循环执行之间的事件循环。你可以这样做:

// utility function to iterate a loop, but have a short `setTimeout()` 
// pause between each loop iteration
// assumes mainFn is a synchronous operation
// doneFn is optional and, if present, will be called when all iterations are done
function iterateWithRepaint(start, num, incr, mainFn, doneFn){
    var end = start + num;
    function next() {
        if (start < end) {
            mainFn(start);
            start += incr;
            setTimeout(next, 20);
        } else {
            if (typeof doneFn === "function") {
                doneFn();
            }
        }
    }
    next();
}

// got rid of callback in here since it was not helpful
// and added loop index as an argument
function updateprogressbar(id, val, label, i) {
    var time = 0;
    $( "#progressbar" ).progressbar({
        value: val
    });
    $("#EMAIL_ALL").val($("#EMAIL_ALL").val()+","+getmeadescriptor($.trim(emails[i])));
    $( "#progress-label"+id ).html("Overall Progress: "+label);
}    

// call your function with a timer delay between each iteration
iterateWithRepaint(0, emails.length, 1, function(i) {
    var howmuchdone = Math.ceil(totalchecked*100/totaltocheck);
    updateprogressbar(1,howmuchdone,howmuchdone+"%", i);
    totalchecked = totalchecked + 1;
});

有一些其他方法可以触发重排,有时通过请求某些CSS属性进行重新绘制,这些属性只能在完全重新布局后准确报告,以便浏览器强制重新布局,但这种方法已被证明是一个难以捉摸的移动目标因为为了提高性能,浏览器一直试图优化他们认为不必要的重绘。所以,我选择不依赖这些技术。

在循环代码披露之前的先前答案:

您的setTimeout()声明不正确,但问题还不止于此。

即使正确声明,setTimeout()本身也不允许您的网页引用。所做的就是安排一些代码在将来运行。相反,您必须立即停止运行代码并将未来的代码放入setTimeout回调中。只有这样才能让当前线程返回事件循环,然后给屏幕重新绘制的机会。

仅供参考,您的setTimeout()应该是这样的:

setTimeout(func,time);

但是在您显示的代码中更改它将无法解决任何问题,因为func()显示它实际上并没有做任何事情,因此实际上不会允许重新绘制。如果您将来的所有代码都在func() AND之内,那么在调用setTimeout()之后,当前执行线程中没有更多代码可以运行,那么它可能会允许重新绘制。

如果您向我们展示了延迟重新绘制的其余代码,那么我们可以更具体地帮助您。您展示的代码不会延迟重新绘制。