如何在Javascript中执行不阻止UI的循环?

时间:2014-06-12 20:41:12

标签: javascript

我的页面上有一些javascript需要很长时间才能执行(10-30秒之间)

代码基本上是这样的:

//Iterate over all the elements in the array
for(int i=0; i<array.length; i++){
   //Complex stuff for each element
}

问题在于,在执行此代码时,UI没有响应。

有什么方法可以解决这个问题吗?我知道javascript有某种异步但我从来没有真正使用过它......

此外,处理元素的顺序必须始终是它们在数组中出现的顺序。

编辑:我尝试使用setTimeout()并在&#34;复制&#34;中尝试下面的解决方案,但它仍然无法解决我的问题。我想这是因为我的数组的大小不是很大但是每个元素的计算量都很大。

我想要响应的UI也是一个动画加载的gif。由于每个项目的计算太大,动画都很草率。

6 个答案:

答案 0 :(得分:4)

创建一个变量并将其设置为计数器的起始值。

创建一个函数:

  1. 无论循环体是什么
  2. 递增计数器
  3. 测试计数器是否仍在长度内,如果是,则再次调用该功能
  4. 此时您将拥有与您已有的功能相同的功能。

    要在每次调用函数之间暂停,并允许其他函数触发时间,请通过调用setTimeout替换对函数的直接调用,并将该函数用作第一个参数。

    var counter = 0;
    function iterator () {
        //Complex stuff for each element
        counter++;
        if (counter < array.length) {
            setTimeout(iterator, 5);
        } 
    }
    

答案 1 :(得分:2)

Javascript使用称为“事件循环”的东西。基本上,有一个线程在每个事件发生时执行代码。

有一个名为setTimeout的函数可用于使您的Javascript代码在循环的未来迭代中运行。在长时间运行的Javascript任务期间,一些开发人员用来保持UI响应的一个技巧是定期调用要以超时零运行的代码。这导致代码几乎立即执行而不锁定事件循环。

例如,你可以写

var i = 0;
setTimeout(myFunction, 0);

function myFunction() {
    // complex stuff for each element.
    i++;
    if (i < array.length)
        setTimeout(myFunction, 0);
}

请注意,在现代浏览器中,只要Javascript代码正在执行,UI就会无法响应。有些浏览器甚至会弹出一条消息,敦促用户杀死脚本;非常糟糕!

此外,在现代浏览器中,有一个名为“Web Workers”的新功能。 Web Workers允许在单独的线程中执行Javascript代码。您可以根据需要创建任意数量的线程,因此如果您的任务可以轻松并行化,请考虑使用Web worker。

答案 2 :(得分:1)

如果您需要浏览器在JS代码中间更新页面,则需要向浏览器屈服,例如:使用setTimeout,以延续传递方式:

function doStuff(i) {
    // do some stuff with index i
    if (i < array.length) {
        setTimeout(doStuff.bind(window, i + 1), 0);
    }
}

doStuff(0);

答案 3 :(得分:0)

如果您的任务是在不干扰用户界面的情况下处理数据,则可以使用WebWorkers https://developer.mozilla.org/en/docs/Web/Guide/Performance/Using_web_workers

请注意现代浏览器支持它。

答案 4 :(得分:0)

我倾向于做这样的事情:

function doStuff(arr) {
  if (arr.length === 0) {
    // do whatever you need to do when you're done
    return;
  }

  // Do body of computation here

  // Or use setTimeout if your browser doesn't do setImmediate
  setImmediate(function () { doStuff(arr.slice(1)); });
}

doStuff(actualArrayOfThings);

基本上,打破你的循环体,并使用递归按顺序运行循环的每个部分。将其分解为辅助函数非常容易,因为循环体本身就是变化的。类似的东西:

function eachAsync(arr, body) {
  if (arr.length === 0) {
    // do whatever you need to do when you're done
    return;
  }

  body(arr[0]);

  setImmediate(function () { eachAsync(arr.slice(1), body); });
}

eachAsync(actualArrayOfThings, doStuff);

答案 5 :(得分:-1)

如果for循环正在做一些整洁的工作,请使用setTimeOut()。它取决于每次迭代执行的复杂内容。

另外,请考虑使用JQuery / AJAX异步执行它。