jQuery/Javascript - 如何在继续执行函数之前等待操作的 DOM 更新

IT技术 javascript jquery dom high-load
2021-03-14 03:20:06

我想要做的是在执行 CPU 密集型脚本(运行需要 3-12 秒,没有 AJAX)之前更新一个简单的 div 说“处理...”然后更新 div 说“完成! ” 完成后。

我所看到的是 div 永远不会更新为“正在处理...”。如果我在该命令之后立即设置断点,则 div 文本会更新,所以我知道语法是正确的。在 IE9、FF6、Chrome13 中的行为相同。

即使绕过 jQuery 并使用基本的原始 Javascript,我也看到了同样的问题。

你会认为这会有一个简单的答案。然而,由于 jQuery .html() 和 .text() 没有回调钩子,这不是一个选项。它也不是动画的,所以没有 .queue 可以操作。

您可以使用我在下面准备的示例代码自行测试,该示例代码显示了具有 5 秒高 CPU 功能的 jQuery 和 Javascript 实现。代码很容易理解。当您单击按钮或链接时,您永远不会看到“正在处理...”

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" ></script>
<script type="text/javascript">
function addSecs(d, s) {return new Date(d.valueOf()+s*1000);}
function doRun() {
    document.getElementById('msg').innerHTML = 'Processing JS...';
    start = new Date();
    end = addSecs(start,5);
    do {start = new Date();} while (end-start > 0);
    document.getElementById('msg').innerHTML = 'Finished JS';   
}
$(function() {
    $('button').click(function(){
        $('div').text('Processing JQ...');  
        start = new Date();
        end = addSecs(start,5);
        do {start = new Date();} while (end-start > 0);
        $('div').text('Finished JQ');   
    });
});
</script>
</head>
<body>
    <div id="msg">Not Started</div>
    <button>jQuery</button>
    <a href="#" onclick="doRun()">javascript</a>
</body>
</html>
4个回答

将其设置为处理,然后执行 setTimeout 以防止 CPU 密集型任务在 div 更新后运行。

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" ></script>
<script>
function addSecs(d, s) {return new Date(d.valueOf()+s*1000);}
function doRun() {
    document.getElementById('msg').innerHTML = 'Processing JS...';
    setTimeout(function(){
         start = new Date();
         end = addSecs(start,5);
         do {start = new Date();} while (end-start > 0);
         document.getElementById('msg').innerHTML = 'Finished Processing';   
    },10);
}
$(function() {
    $('button').click(doRun);
});    
</script>
    </head>
<body>
    <div id="msg">Not Started</div>
    <button>jQuery</button>
    <a href="#" onclick="doRun()">javascript</a>
</body>
</html>

您可以根据需要修改 setTimeout 延迟,对于较慢的机器/浏览器,它可能需要更大。

编辑:

您还可以使用警报或确认对话框来允许页面时间更新。

document.getElementById('msg').innerHTML = 'Processing JS...';
if ( confirm( "This task may take several seconds. Do you wish to continue?" ) ) {
     // run code here
}
伟大的!setTimeout 允许渲染完成。希望不会有超过 10 毫秒的情况。(我想延长到 100 毫秒不会有什么坏处。)虽然对话框解决方案也解决了这个问题,但它增加了不必要且烦人的额外点击。
2021-04-21 03:20:06
@Brett84c 同步的事实不会改变任何东西。它会立即更改 dom,但在调用堆栈清除之前,UI 不会更新。在长时间的 CPU 密集型任务之后,ui 才会更新。
2021-05-03 03:20:06
对。当您执行 dom 更新后跟一个 alert('foo') 时,您可以观察到同样的事情。警报将弹出,然后在您关闭警报后,dom 更新将呈现。发生这种情况是因为警报是同步的,很像上面的 CPU 密集型任务。
2021-05-04 03:20:06
-1 因为这实际上是一个竞争条件。即使在这里的快速机器上,也不能保证经过的时间会低于 10 毫秒......
2021-05-10 03:20:06
@yerforkferchips 是的,由于事件循环的工作方式,它确实可以保证它。我们所要做的就是将回调推送到回调队列中,以便它在下一次事件循环运行之前不会运行。它甚至可以小到 0 毫秒并且仍然可以在任何地方工作,因为渲染器(也被放入回调队列)具有优先权。
2021-05-16 03:20:06

您有一个循环运行 5 秒钟并在此期间冻结 Web 浏览器。由于 Web 浏览器已冻结,因此无法进行任何渲染。您应该使用setTimeout()而不是循环,但我假设循环只是替代需要一段时间的 CPU 密集型函数?您可以使用 setTimeout 在执行您的函数之前让浏览器有机会进行渲染:

jQuery:

$(function() {
    $('button').click(function(){
        (function(cont){
            $('div').text('Processing JQ...');  
            start = new Date();
            end = addSecs(start,5);
            setTimeout(cont, 1);
        })(function(){
            do {start = new Date();} while (end-start > 0);
            $('div').text('Finished JQ');   
        })
    });
});

香草JS:

document.getElementsByTagName('a')[0].onclick = function(){
    doRun(function(){
         do {start = new Date();} while (end-start > 0);
         document.getElementById('msg').innerHTML = 'Finished JS';   
    });
    return false;
};

function doRun(cont){
    document.getElementById('msg').innerHTML = 'Processing JS...';
    start = new Date();
    end = addSecs(start,5);
    setTimeout(cont, 1);
}

您还应该记住始终使用 var 关键字声明所有变量,并避免将它们暴露给全局范围。这是一个 JSFiddle:

http://jsfiddle.net/Paulpro/ypQ6m/

是的,循环只是为了表示 CPU 密集型功能。这并不打算作为生产代码的表示,只是一个演示问题的快速片段。
2021-04-30 03:20:06

我不得不等待 jQuery 操作 DOM,然后抓取更改(将表单字段多次加载到表单中,然后提交)。DOM 增长,插入花费的时间越来越长。我使用 ajax 保存了更改,以便用户能够从他停止的地方继续。

这没有按预期工作

jQuery('.parentEl').prepend('<p>newContent</p>');
doSave(); // wrapping it into timeout was to short as well sometimes

由于jQuery的功能,喜欢.prepend()做继续做连锁,只有当,下面似乎这样的伎俩

jQuery('.parentEl').prepend('<p>newContent</p>').queue(function() {
  doSave();
});

截至 2019 年,使用double requesAnimationFrame跳过一帧,而不是使用 setTimeout 创建竞争条件。

....
function doRun() {
    document.getElementById('msg').innerHTML = 'Processing JS...';
    requestAnimationFrame(() =>
    requestAnimationFrame(function(){
         start = new Date();
         end = addSecs(start,5);
         do {start = new Date();} while (end-start > 0);
         document.getElementById('msg').innerHTML = 'Finished Processing';   
    }))
}
...
这让我很困惑
2021-04-23 03:20:06
@TangMonk 你能说一下缺少什么吗,以便我可以更新这个 - 例如什么让你感到困惑?
2021-04-30 03:20:06