Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/466.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript innerHTML可以';不可信:不总是同步执行_Javascript - Fatal编程技术网

Javascript innerHTML可以';不可信:不总是同步执行

Javascript innerHTML可以';不可信:不总是同步执行,javascript,Javascript,要查看实际问题,请参阅。单击按钮会触发按钮Handler(),如下所示: function buttonHandler() { var elm = document.getElementById("progress"); elm.innerHTML = "thinking"; longPrimeCalc(); } 您可能希望此代码将div的文本更改为“thinking”,然后运行longPrimeCalc(),这是一个需要几秒钟才能完成的算术函数。然而,事实并非如此。相反,“lon

要查看实际问题,请参阅。单击按钮会触发
按钮Handler()
,如下所示:

function buttonHandler() {
  var elm = document.getElementById("progress");
  elm.innerHTML = "thinking";
  longPrimeCalc();
}
您可能希望此代码将div的文本更改为“thinking”,然后运行
longPrimeCalc()
,这是一个需要几秒钟才能完成的算术函数。然而,事实并非如此。相反,“longPrimeCalc”首先完成,然后文本在运行完成后更新为“thinking”,就像两行代码的顺序颠倒了一样

看起来浏览器并没有同步运行“innerHTML”代码,而是为其创建一个新线程,该线程在空闲时执行

我的问题是:

  • 引擎盖下发生了什么导致了这种行为
  • 如何让浏览器按预期的方式运行,即在执行“
    longPrimeCalc()
    ”之前强制它更新“innerHTML”

  • 我在最新版本的chrome上对此进行了测试。

    你的猜测是错误的。
    .innerHTML
    更新会同步完成(而且浏览器肯定不会创建新线程)。在代码完成之前,浏览器根本不需要更新窗口。如果要以某种需要更新视图的方式查询DOM,那么浏览器将别无选择

    例如,在设置
    innerHTML
    之后,添加以下行:

    var sz = elm.clientHeight; // whoops that's not it; hold on ...
    

    编辑-我可能会想出一种欺骗浏览器的方法,或者这是不可能的;确实,在单独的事件循环中启动长时间计算将使其工作:

    setTimeout(longPrimeCalc, 10); // not 0, at least not with Firefox!
    

    这里一个很好的教训是,浏览器努力避免对页面布局进行无意义的重新流动。如果您的代码在素数休假时出错,然后再次回来更新
    innerHTML
    ,那么浏览器将保存一些无意义的工作。即使它没有绘制更新的布局,浏览器仍然必须弄清楚DOM发生了什么,以便在查询元素大小和位置时提供一致的答案

  • 我认为它的工作方式是先完成当前运行的代码,然后完成所有页面更新。在这种情况下,调用
    longPrimeCalc
    会导致执行更多的代码,并且只有在完成后页面更新才会更改

  • 要解决此问题,必须终止当前运行的代码,然后在另一个上下文中开始计算。您可以使用
    setTimeout
    来完成此操作。我不确定除此之外还有没有别的办法

  • 下面是一个显示该行为的示例。您不必将回调传递给
    longPrimeCalc
    ,只需创建另一个函数,该函数根据您的需要处理返回值。基本上,您希望将计算推迟到另一个“线程”执行。以这种方式编写代码可以让您清楚地知道自己在做什么(再次更新以使其可能更好):


    setTimeout(longPrimeCalc,50)将解决此问题。而且:“但是创建了一个新线程”不,js只有一个活动线程。
    0
    而不是
    50
    也是有效的(而且更自我解释)。@Xaerxess是的,但在这种特殊情况下,Firefox利用了这一点,并继续推迟重新流程@尖锐的Oops,在我的代码库中似乎几乎没有可能的bug:(应该注意的是,这一切都与
    .innerHTML
    没有任何关系。对于任何DOM操作,您都会得到与预期重画相同的行为。您有没有需要更新视图的示例?那么我如何按照您的建议进行查询:)我尝试放置
    控制台.log(elm.innerHTML);
    在两行代码之间,但这没有任何效果。也就是说,我的问题的答案是什么?#2?@MartinSmith我只是想找一些简单的东西;我会更新answer@Jonah答案更新-关键是询问浏览器一些只有在重做时才能回答的问题layout@Pointy,我仍然看到同样的行为IOR和以前一样:(1,你没有给现有的答案和评论添加任何东西。”Gordon:Jonah说:“考虑在“LoimPrimeCalc”计算中显示“思考”的更真实的世界用法,并在完成后返回到Orrg文本。现在StimeTimeOn首先要调用“LimePrimeCalc”。“,然后使用一些函数将文本返回到orig。或者更糟的是,我可以通过longPrimeCalc进行回调。我真的希望有更好的方法…”在他的评论中。这是一个解决方案,但这两种方法都不起作用。@Claudiu,我没有投你反对票,但这对我不起作用,也就是说,它没有显示“思考”“在计算过程中。这实际上意味着这个setTimeout方法甚至不能作为黑客手段工作……所以,这确实有助于讨论。@乔纳:哦,有趣。我想你试过JS小提琴?你到底在使用什么浏览器?@Claudiu,哈,实际上它甚至在你将1改为2时开始工作
    function defer(f, callback) {
      var proc = function() {
        result = f();
        if (callback) {
          callback(result);
        }
      }
      setTimeout(proc, 50);
    }
    
    function buttonHandler() {
      var elm = document.getElementById("progress");
      elm.innerHTML = "thinking...";
      defer(longPrimeCalc, function (isPrime) {
        if (isPrime) {
          elm.innerHTML = "It was a prime!";
        }
        else {
          elm.innerHTML = "It was not a prime =(";
        }
      });
    }