Skip to content

JS 执行 100 万个函数

web workers

可以考虑使用web workers来将这些任务从主线程中分离出来

webworkers允许在后台线程中运行脚本,从而避免阻塞主线程,保持页面的响应性

js
// 主线程
const worker=new Worker('worker.js');//创建新的web worker

worker.postMessage({start:0, end: 1000000});

worker.onmessage=function(event){
    const result=event.data;
    console.log('任务完成',result);
};

// worker.js代码

onmessage=function(event){
    const start=event.data.start;
    const end=event.data.end;
    let sum=0
    for(let i=start;i<=end;i++){
        sum+=i;
    }
    postMessage(sum);//像主线程发送消息
}

requestAnimationFrame实现任务调度

本质是利用requestAnimationFrame对大批量的执行任务进行分割,requestAnimationFrame的调用频率通常与显示器的刷新率相匹配(60Hz,每秒60次),这样可以确保动画在不同设备上都能保持流畅

js
// 假设有一个包含大量元素的数组
const bigArray = Array.from({ length: 1000000 }, (_, i) => i + 1);

// 定义一个处理函数,例如对数组中的每个元素进行平方操作
function processChunk(chunk) {
  return chunk.map(num => num * num);
}

// 分割任务并使用requestAnimationFrame
const chunkSize = 1000; // 每个小块的大小
let index = 0;

function processArrayWithRAF() {
  function processChunkWithRAF() {
    const chunk = bigArray.slice(index, index + chunkSize); // 从大数组中取出一个小块
    const result = processChunk(chunk); // 处理小块任务
    console.log('处理完成:', result);
    index += chunkSize;

    if (index < bigArray.length) {
      requestAnimationFrame(processChunkWithRAF); // 继续处理下一个小块
    }
  }

  requestAnimationFrame(processChunkWithRAF); // 开始处理大数组
}

processArrayWithRAF();

动态调整 chunkSize 的大小

js
  const $result = document.getElementById("result");

// 假设有一个包含大量元素的数组
const bigArray = Array.from({ length: 1000000 }, (_, i) => i + 1);

// 定义一个处理函数,对数组中的每个元素执行一次
function processChunk(chunk) {
  return `chunk: ${chunk}`;
}

// 动态调整 chunkSize 的优化方式
let chunkSize = 1000; // 初始的 chunkSize
let index = 0;

function processArrayWithDynamicChunkSize() {
  function processChunkWithRAF() {
    let startTime = performance.now(); // 记录结束时间
    for (let i = 0; i < chunkSize; i++) {
      if (index < bigArray.length) {
        const result = processChunk(bigArray[index]); // 对每个元素执行处理函数
        $result.innerText = result;
        index++;
      }
    }
    let endTime = performance.now();
    let timeTaken = endTime - startTime; // 计算处理时间

    // 根据处理时间动态调整 chunkSize
    if (timeTaken > 16) { // 如果处理时间超过一帧的时间(16毫秒),则减小 chunkSize
      chunkSize = Math.floor(chunkSize * 0.9); // 减小10%
    } else if (timeTaken < 16) { // 如果处理时间远小于一帧的时间(8毫秒),则增加 chunkSize
      chunkSize = Math.floor(chunkSize * 1.1); // 增加10%
    }

    if (index < bigArray.length) {
      requestAnimationFrame(processChunkWithRAF); // 继续处理下一个小块
    }
  }

  requestAnimationFrame(processChunkWithRAF); // 开始处理大数组
}

processArrayWithDynamicChunkSize();

requestIdleCallback

window.requestIdleCallback是一个用于在浏览器空闲时执行任务的API,允许开发者在浏览器的主线程空闲时执行一些任务,而不会影响用户界面的流畅性和响应性

requestIdleCallback接受一个回调函数作为参数,该回调函数会在浏览器空闲时被调用

js
window.requestIdleCallback(function(deadline) {
  // 在空闲时执行的任务
  // deadline 参数提供了一些信息,比如剩余的空闲时间等
});

还有一个配套的 window.cancelIdleCallback 方法,用于取消通过 requestIdleCallback 请求的回调

js
const id = window.requestIdleCallback(function(deadline) {
  // 在空闲时执行的任务
});

// 取消回调
window.cancelIdleCallback(id);

requestIdleCallback 并不是所有浏览器都支持的标准,因此在使用时要注意检查浏览器的兼容性。

参考

https://github.com/pro-collection/interview-question/issues/672

https://github.com/yanlele/node-index/tree/master/books/%E7%9F%A5%E8%AF%86%E5%BA%93/01%E3%80%81%E5%89%8D%E7%AB%AF%E6%8A%80%E6%9C%AF%E7%9F%A5%E8%AF%86/29.100%E4%B8%87%E4%B8%AA%E5%87%BD%E6%95%B0%E6%89%A7%E8%A1%8C%E4%BF%9D%E8%AF%81%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%8D%E5%8D%A1