setTimeout可以保存我的循环函数不被视为无响应吗?

时间:2012-07-03 03:18:59

标签: javascript for-loop settimeout nested-loops

我有一些过程密集的Javascript代码,它触发了“无响应脚本”警告。每个编码步骤必须按照它们被编码的顺序发生。我想我找到了有问题的功能,但我不明白如何在不触发警告的情况下使其工作。

我发现了一些可能对此有帮助的迹象(setTimeout) Javascript: Unresponsive script error 但这真的很模糊,所以我一直在寻找。这是一个更好的例子,但我无法在我的代码中看到实现这个的方法.. How can I give control back (briefly) to the browser during intensive JavaScript processing? 原文 http://www.julienlecomte.net/blog/2007/10/28/ 无论如何都是天才,但我似乎无法在这里实现它。

以下是我认为导致合适的代码。

// -----------------------------------
//  Output stats for all nations
//  by continent in a colon delimited
//  list further delimited by a ~
//  ( ContinentName:NationPower:NationHTML )
//  expects the output from the 
//  continent_stats function
// -----------------------------------

function Nations(ContinentData) {

    document.write("<tr><th>Nation Stats</th></tr><tr>"); // setup progress bar
    var NationString = ""; // init the string
    var Carray = ContinentData.split("*"); //continents
    for (cit = 0; cit < Carray.length; cit++) { // go through the continents
        var Cstat = Carray[cit].split(":"); // make an array of the individual continent stats
        var NumberOfNations = Cstat[4]; // nation #
        var ContinentName1 = Cstat[0]; // Continent Name
        document.write("<td class='ProgressBarText'>" + ContinentName1 + "</td><td class='ProgressBars'>"); // Format loader screen text
        for (nnum = 0; nnum < NumberOfNations; nnum++) { // go through the number of nations on the continent
            var nat1 = BuildCulture(); // build the nation
            var Natname= "Nation"+padLeft(nnum,2,"0"); // name the nation
            NationString = NationString + ContinentName1 + ":" + Natname + ":" + nat1 + "~"; // build the string
            document.write("█"); // add to progress bar
        }
        document.write("</td><td>"+NumberOfNations+ " Nations</td></tr>"); // progress bar complete
    }
    document.write("</table>"); // end the loader screen table
    // send the nation string back
    return NationString;
}

所以你可以看到它在各大洲之间循环并为每个大陆创造了国家。 BuildCulture()函数是罪魁祸首。它本身就可以很好地工作,但是在大约4个大陆的过程中,8或9串在一起,并且警告响起。

我尝试过使用

setTimeout( function() { BuildCulture(); }, 1000);

遍布整个地方,在主代码部分,在BuildCulture()函数的开头和结尾,在进入和退出循环的Nations(ContinentData)函数中。它永远不会奏效。

我显然循环太多但我需要每个循环。 SetTimeout会帮助我,还是我在追逐错误的陈述?

如果SetTimeout是我的解决方案的目标,我该如何在此代码中实现它?

非常感谢。

P.S。 我的目标只是在Firefox中工作,因此不需要与IE核心浏览器兼容。

2 个答案:

答案 0 :(得分:0)

首先,浏览器是单线程应用程序。所有事件,计时器和交互都是线性的,一步一步完成的。因此,您无法生成新线程或进行并行计算。

从这个意义上说,如果你可以剪切function Nations并在setTimeout中调用它来提供前一个结果,那么setTimeout可以假设有用。但这不是最佳的;)


其次,请勿触摸DOM !!!
JS可能很快,但触摸DOM(getElements,document.write,innerHTML ......)是 SLOW

在你的情况下,重写function Nations(ContinentData)输出一个你将innerHTML的字符串输出到某个dom元素。
重点是所有的计算和准备都将在(相对)快速的JS引擎中完成,结果将被应用或者内部HTML编译为非常慢的DOM。

或者您可以使用DOM对象使用createElem和addChild。无所谓。两种方式都有优点和缺点,但两者都做同样的事情。


第三:尝试网络工作者。他们在模拟'线程'方面做得很好:
http://ejohn.org/blog/web-workers/
http://www.sitepoint.com/javascript-threading-html5-web-workers/
http://www.w3schools.com/html5/html5_webworkers.asp

答案 1 :(得分:0)

好的,因为我无法在任何地方运行,可能会出现一些错误 - 但如果您需要使用超时,这就是您想要的模式。

然而,考虑到结构和你的代码风格,我得到的印象是你可能会以错误的方式解决这个问题。

例如,您的数据来自哪里?为什么结构化数据用字符串表示?你在客户端进行所有这些计算吗?这是游戏吗?什么是buildCulture()这样做需要这么长时间?无论如何,你应该研究JSON和异步加载(也就是AJAX)。

function Nation(continentData, renderNationComplete) {
  // begin our table
  var str = '<table>',
      continents = continentData.split('*');

  // use the `+=` operator to add string together
  str += '<tr><th>Nation Stats</th></tr><tr>';

  // This will act like a `for` loop - 
  // the difference is that we can control the iterations
  var continentsCount = 0;
  function renderContinents(){
    if(continentsCount < continents.length) {
      var stats = continent[continentsCount].split(':'),
          nNations = stats[4],
          cName = stats[0];

      str += '<td class="ProgressBarText">' + cName + '</td><td class="ProgressBars">';

      var nationCount = 0;
      function renderNation(){
        if(nationCount < nNations) {
          var culture = BuildCulture(),
              nName = "Nation" + padLeft(nationCount, 2, "0");

          str += cName + ':' + nName + ':' + culture + '~';
          str += '█'; // not precisely sure what this is for.

          nationCount++;
          // renderContinents won't proceed till we're done with all the 
          // renderNations for this particular continent.
          setTimeout(function(){ renderNation(); }, 1000);
        } else {
          // close up the rows
          str += '</td><td>' + nNations + ' Nations</td></tr>';
          nationCount++;
          // this timeout may not be necessary, you'll have to tweak the value.
          setTimeout(function(){ renderContinents(); }, 1000);
        }
      } renderNation();
    } else {
      str += '</table>';

      // everything is done, send it back.
      // note: this function does not return anything,
      // instead, it keeps on running asynchronously till
      // it's done, at which point it call the callback
      // that you pass in when it is instantiated.
      renderNationComplete(str);
    }
  } renderContinents();
}

// here we call the Nation function, and pass in another function
// that we want to be called when everything is done.
// in this case that function returns a string of html, that we can
// then add to the DOM.
Nation(data, function(html){
  var el = document.getElementById('tableArea');
  el.innerHtml(html);
});