如何使用javascript中的setTimeOut函数处理大循环而不冻结浏览器?

时间:2016-08-03 05:36:32

标签: javascript jquery asynchronous

我创建了两个按钮。一个名为' sync'和其他名为' async'。点击同步'按钮,它应该使用循环处理大数组,它冻结浏览器,直到循环完成处理更大的数组。当我按下'async' button.it应该处理相同的大型数组而不冻结浏览器。如何使用setTimeOut函数执行此操作?

4 个答案:

答案 0 :(得分:1)

您可以使用循环变量,如下面的代码。在此示例中,函数将每个元素增加1.超时时间为1毫秒。

    var currentIndex;
    function processNthElement(array) {
        if (currentIndex >= array.length)
        {
            //the whole array has been processed, do what you need to do
            //with the results here
            return;
        }

        //do what you want with the array element here
        array[currentIndex]++;

        currentIndex++;

        setTimeout(function () {
            processNthElement(array);
        }, 1);
    }
    function processArrayWithSetTimeout(array) {
        currentIndex = 0;
        processNthElement(array);
    }

然后要处理一个大数组,只需调用processArrayWithSetTimeout(array)。但是,由于我们使用超时,您需要在最后一次函数调用时处理结果(请参阅函数中的注释)。如果数组有10000个元素,则处理时间超过10000毫秒或10秒,但UI不会被冻结。

请注意,这仍会按顺序处理数组,但在处理1个元素后等待一段时间后不会冻结UI。

答案 1 :(得分:0)

Javascript是单线程引擎。如此大的计算将暂时阻止所有其他事件。您可以使用Web worker在后台运行此类大脚本

答案 2 :(得分:0)

您可以使用递归,将setTimeout声明为变量,以便能够“中断”对调用setTimeout()的函数的递归调用

var curr, fn = (arr, i = -1, n = arr[++i]) => 
                 curr = setTimeout(() => {
                          // do stuf with `n`: current index in `arr` array
                          console.log(n);
                          // recursively call `fn` if condition met; e.g., 
                          // `i` less than `arr.length` 
                          i < arr.length ? fn(arr, i) : (curr = void 0)
                        }, 0);

// e.g.
fn(Array(10000).fill(1));
// clear timeout, stop recursion 
clearTimeout(curr);

答案 3 :(得分:0)

在ES7中使用异步/等待,这非常简单。耗时太长的长循环示例

function main() {
  const numOperations = 1000000000;
  let sum = 0;
  for (let i = 0; i < numOperations; ++i) {
    sum += doSomeOperation(i);
  }
  console.log(sum);
}
main();

function doSomeOperation(v) {
  return v * 1.1 / (v + 1);
}

使用async / await使其偶尔出现在浏览器中的示例

async function main() {
  const numOperations =  1000000000;
  const iterationsPerChunk = 10000000;
  let sum = 0;
  for (let i = 0; i < numOperations; ++i) {
    if (i % iterationsPerChunk === 0) {
       await oneMoment();
    }
    sum += doSomeOperation(i);
  }
  console.log(sum);
}
main();

function doSomeOperation(v) {
 return v * 1.1 / (v + 1);
}

function oneMoment() {
  return new Promise((resolve) => {
    setTimeout(resolve);
  });
}

iterationsPerChunk选择一个好的值可能会更困难。您可以轻松地创建一些类来检查performance.now并仅在经过一定时间(例如1/2秒或1秒)后才等待调用。每次致电setTimeout都会产生5毫秒到20毫秒的时间,因此您不想等待太久,但是确实使它易于使用。