DOM 刷新长时间运行的函数

新手上路,请多包涵

我有一个按钮,当它被点击时,它会运行一个长时间运行的函数。现在,当函数运行时,我想更改按钮文本,但我在某些浏览器(如 Firefox、IE)中遇到问题。

HTML:

 <button id="mybutt" class="buttonEnabled" onclick="longrunningfunction();"><span id="myspan">do some work</span></button>

javascript:

 function longrunningfunction() {
    document.getElementById("myspan").innerHTML = "doing some work";
    document.getElementById("mybutt").disabled = true;
    document.getElementById("mybutt").className = "buttonDisabled";

    //long running task here

    document.getElementById("myspan").innerHTML = "done";
}

现在这在 firefox 和 IE 中有问题,(在 chrome 中它工作正常)

所以我想把它放到一个settimeout中:

 function longrunningfunction() {
    document.getElementById("myspan").innerHTML = "doing some work";
    document.getElementById("mybutt").disabled = true;
    document.getElementById("mybutt").className = "buttonDisabled";

    setTimeout(function() {
        //long running task here
        document.getElementById("myspan").innerHTML = "done";
    }, 0);
}

但这对 Firefox 也不起作用!该按钮被禁用,改变颜色(由于应用了新的 css)但文本没有改变。

我必须将时间设置为 50 毫秒而不是 0 毫秒,以使其工作(更改按钮文本)。至少现在我觉得这很愚蠢。我能理解它是否只需要 0 毫秒的延迟就可以工作,但是在速度较慢的计算机上会发生什么?也许 firefox 在 settimeout 中需要 100ms?这听起来很愚蠢。试了很多次,1ms,10ms,20ms……不行,刷新不了。只有 50 毫秒。

所以我遵循了本主题中的建议:

javascript dom操作后在Internet explorer中强制刷新DOM

所以我试过:

 function longrunningfunction() {
    document.getElementById("myspan").innerHTML = "doing some work";
    var a = document.getElementById("mybutt").offsetTop; //force refresh

    //long running task here

    document.getElementById("myspan").innerHTML = "done";
}

但它不起作用(FIREFOX 21)。然后我尝试了:

 function longrunningfunction() {
    document.getElementById("myspan").innerHTML = "doing some work";
    document.getElementById("mybutt").disabled = true;
    document.getElementById("mybutt").className = "buttonDisabled";
    var a = document.getElementById("mybutt").offsetTop; //force refresh
    var b = document.getElementById("myspan").offsetTop; //force refresh
    var c = document.getElementById("mybutt").clientHeight; //force refresh
    var d = document.getElementById("myspan").clientHeight; //force refresh

    setTimeout(function() {
        //long running task here
        document.getElementById("myspan").innerHTML = "done";
    }, 0);
}

我什至试过 clientHeight 而不是 offsetTop 但什么也没有。 DOM 不会刷新。

有人可以提供可靠的解决方案,最好是 non-hacky 吗?

提前致谢!

正如这里所建议的,我也尝试过

$('#parentOfElementToBeRedrawn').hide().show();

无济于事

在 Chrome/Mac 上强制 DOM 重绘/刷新

长话短说:

寻找一种可靠的跨浏览器方法来强制 DOM 刷新而不使用 setTimeout(首选解决方案,因为需要不同的时间间隔,具体取决于长时间运行代码的类型、浏览器、计算机速度和 setTimeout 需要 50 到 100 毫秒,具体取决于视情况而定)

jsfiddle:http: //jsfiddle.net/WsmUh/5/

原文由 MirrorMirror 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 234
2 个回答

解决了! 没有 setTimeout()!!!

在 Chrome 27.0.1453、Firefox 21.0、Internet 9.0.8112 中测试

$("#btn").on("mousedown",function(){
$('#btn').html('working');}).on('mouseup', longFunc);

function longFunc(){
  //Do your long running work here...
   for (i = 1; i<1003332300; i++) {}
  //And on finish....
   $('#btn').html('done');
}

在这里演示!

原文由 Kylie 发布,翻译遵循 CC BY-SA 3.0 许可协议

网页基于单线程控制器更新,一半的浏览器在 JS 执行停止之前不会更新 DOM 或样式,将计算控制权交还给浏览器。这意味着如果您设置一些 element.style.[...] = ... 它不会启动,直到您的代码运行完成(完全运行,或者因为浏览器看到您正在做的事情让它拦截处理几毫秒)。

您有两个问题:1) 您的按钮中有一个 <span> 。删除它,只需在按钮本身上设置 .innerHTML 。但这当然不是真正的问题。 2)你正在运行很长的操作,你应该非常认真地思考为什么,在回答了为什么之后,如何:

如果您正在运行一项长时间的计算作业,请将其分解为超时回调(或者,在 2019 年,等待/异步——请参阅本答案末尾的注释)。你的例子没有显示你的“长期工作”实际上是什么(自旋循环不算)但是你有几个选项取决于你使用的浏览器,一个 巨大的 书注:不要在 JavaScript 中运行长时间的工作,期间. JavaScript 按照规范是单线程环境,所以你想做的任何操作都应该能够在几毫秒内完成。如果不能,那么您确实做错了什么。

如果您需要计算困难的事情,请使用 AJAX 操作(跨浏览器通用,通常为您提供 a)更快的处理该操作和 b)30 秒的时间,您可以异步地等待要返回的结果)或使用 webworker 后台线程(非常不通用)。

如果您的计算花费了很长时间但并非如此荒谬,请重构您的代码以便您执行部分,并有超时喘息空间:

 function doLongCalculation(callbackFunction) {
  var partialResult = {};
  // part of the work, filling partialResult
  setTimeout(function(){ doSecondBit(partialResult, callbackFunction); }, 10);
}

function doSecondBit(partialResult, callbackFunction) {
  // more 'part of the work', filling partialResult
  setTimeout(function(){ finishUp(partialResult, callbackFunction); }, 10);
}

function finishUp(partialResult, callbackFunction) {
  var result;
  // do last bits, forming final result
  callbackFunction(result);
}

长时间的计算几乎总是可以重构为几个步骤,因为您正在执行多个步骤,或者因为您正在运行相同的计算一百万次,并且可以将其分成几批。如果你有(夸大)这个:

 var resuls = [];
for(var i=0; i<1000000; i++) {
  // computation is performed here
  if(...) results.push(...);
}

然后你可以简单地将它分解成一个带有回调的超时松弛函数

function runBatch(start, end, terminal, results, callback) {
  var i;
  for(var i=start; i<end; i++) {
    // computation is performed here
    if(...) results.push(...);      }
  if(i>=terminal) {
    callback(results);
  } else {
    var inc = end-start;
    setTimeout(function() {
      runBatch(start+inc, end+inc, terminal, results, callback);
    },10);
  }
}

function dealWithResults(results) {
  ...
}

function doLongComputation() {
  runBatch(0,1000,1000000,[],dealWithResults);
}

TL;DR :不要运行长时间的计算,但如果必须这样做,让服务器为您完成工作并只使用异步 AJAX 调用。服务器可以更快地完成工作,并且您的页面不会阻塞。

如何在客户端处理 JS 中的长计算的 JS 示例仅在这里解释如果您没有选择执行 AJAX 调用的选项,您可能如何处理这个问题,这在 99.99% 的时间不会是案子。

编辑

另请注意,您的赏金描述是 XY 问题 的经典案例

2019年编辑

在现代 JS 中,await/async 概念极大地改进了超时回调,因此请改用它们。任何 await 让浏览器知道它可以安全地运行计划的更新,因此您以“结构化好像它是同步的”方式编写代码,但您将函数标记为 async ,然后你 await 每当你调用它们时它们都会输出:

 async doLongCalculation() {
  let firstbit = await doFirstBit();
  let secondbit = await doSecondBit(firstbit);
  let result = await finishUp(secondbit);
  return result;
}

async doFirstBit() {
  //...
}

async doSecondBit...

...

原文由 Mike ‘Pomax’ Kamermans 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题