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

Amu*_*ran 1 javascript jquery asynchronous

我创建了两个按钮。一个名为“同步”,另一个名为“异步”。当我单击“同步”按钮时,它应该使用循环处理大数组,并冻结浏览器,直到循环完成处理更大的数组。当我按下“异步”按钮时。它应该处理相同的大数组而不冻结浏览器。如何使用 setTimeOut 函数执行此操作?

gma*_*man 5

在 ES7 中使用 async/await 这变得非常简单。花费太长时间的长循环示例

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);
}
Run Code Online (Sandbox Code Playgroud)

使用 async/await 使其偶尔屈服于浏览器的示例

async function main() {
  const numOperations =  1000000000;
  const iterationsPerChunk = 10000000;
  let sum = 0;
  for (let i = 0; i < numOperations; ++i) {
    if (i && 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));
}
Run Code Online (Sandbox Code Playgroud)

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

示例使用performance.now

async function main() {
  const numOperations =  1000000000;
  let sum = 0;
  let then = performance.now();
  for (let i = 0; i < numOperations; ++i) {
  
    // calling performance.now is slow
    // so only check every 1000 iterations
    if (i && i % 1000 === 0) {
      const now = performance.now();
      
      // have 0.1 second elapsed?
      if (now - then > 100) {
        await oneMoment();
        then = performance.now();
      }
    }
    sum += doSomeOperation(i);
  }
  console.log(sum);
}
main();

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

function oneMoment() {
  return new Promise(resolve => setTimeout(resolve));
}
Run Code Online (Sandbox Code Playgroud)

更新:

我听说有传言,时间限制setTimeout可能会被取消,因为您可以通过通过向自己发布消息来解决这些限制postMessage

您仍然遇到同样的问题,postMessage 确实需要一些时间,但通常是微秒或更短。

const pause = (function() {
  let reqId = 0;
  const reqs = new Map();

  window.addEventListener('message', (e) => {
    const resolve = reqs.get(e.data);
    if (resolve) {
      reqs.delete(e.data);
      resolve();
    }
  });

  return _ => new Promise(resolve => {
    const id = reqId++;
    reqs.set(id, resolve);
    window.postMessage(id);
  });
})();

async function main() {
  const numOperations =  1000000000;
  const iterationsPerChunk = 10000;
  const startTime = performance.now();
  let sum = 0;
  for (let i = 0; i < numOperations; ++i) {
    if (i && i % iterationsPerChunk === 0) {
       await pause();
    }
    sum += doSomeOperation(i);
  }
  const elapsedTime = performance.now() - startTime;
  console.log(sum);
  console.log(`elapsedTime: ${(elapsedTime * 0.001).toFixed(2)}seconds`);
}
main();

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

function oneMoment() {
  return new Promise(resolve => setTimeout(resolve));
}
Run Code Online (Sandbox Code Playgroud)