JavaScript性能优化:异步任务与性能优化

一 单线程的枷锁与突破
核心问题

JavaScript主线程需要同时处理:

  • 用户点击/滚动等交互事件

  • 页面渲染更新

  • 复杂计算任务

典型卡顿场景

// 同步执行耗时计算导致界面冻结
function processData() {
  const data = generateLargeDataset(); // 生成10万条数据
  data.forEach(item => {              // 阻塞主线程
    item.value = heavyCalculation(item); 
  });
  renderResults(data); // 此时用户界面已卡死3秒
}
二 Web Workers:后台线程的救赎
基本使用流程

图片

具体实现

主线程代码

// 创建Worker
const worker = new Worker('task.js');

// 发送任务
worker.postMessage({ 
  type: 'CALCULATE',
  data: generateLargeDataset() 
});

// 接收结果
worker.onmessage = e => {
  renderResults(e.data);
};

worker.js 文件内容

self.onmessage = function(e) {
  if(e.data.type === 'CALCULATE') {
    const result = e.data.data.map(heavyCalculation); // 在Worker线程执行
    self.postMessage(result);
  }
};

限制注意

  • 不能操作DOM

  • 需通过postMessage传递数据(数据会被复制而非共享)

三 任务分片:时间切片技术
优化长任务的经典模式
function processInChunks(data, chunkSize = 100) {
  let index = 0;
  
  function doChunk() {
    const end = Math.min(index + chunkSize, data.length);
    while(index < end) {
      processItem(data[index++]); 
    }
    
    if(index < data.length) {
      // 让出主线程控制权
      setTimeout(doChunk, 0); 
      // 或使用更现代的API:
      // requestIdleCallback(doChunk);
    }
  }
  
  doChunk();
}

效果对比

处理方式

总耗时

用户感知卡顿

交互响应延迟

同步处理

2.1秒

严重卡死

超过2000ms

分片处理

2.3秒

无卡顿

小于50ms

四 高频事件优化:防抖与节流
滚动事件优化示例
// 原始版本:滚动时疯狂执行
window.addEventListener('scroll', handleScroll);

// 节流版:每100ms至多执行一次
function throttle(fn, delay) {
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if(now - lastCall >= delay) {
      fn.apply(this, args);
      lastCall = now;
    }
  };
}

// 防抖版:停止滚动后执行
function debounce(fn, delay) {
  let timer;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

// 根据场景选择:
window.addEventListener('scroll', throttle(handleScroll, 100));

适用场景对比

模式

典型案例

实现重点

节流

滚动加载更多

保证周期性执行

防抖

搜索框输入提示

等待操作停顿后执行

五 Promise的陷阱与优化
微任务堆积问题
// 危险的递归微任务
function recursivePromise() {
  return Promise.resolve().then(() => {
    doSomething();
    recursivePromise(); // 微任务队列永远清空不完
  });
}

优化策略

// 正确:适时让出控制权
async function optimizedLoop() {
  while(condition) {
    await doWork();
    // 每100次迭代插入宏任务
    if(counter % 100 === 0) {
      await new Promise(resolve => setTimeout(resolve, 0));
    }
  }
}
六 实战:图片懒加载综合优化

完整方案要点

  1. 使用Intersection Observer API监听可视区域

  2. 滚动时使用节流处理

  3. Web Worker预解码图片数据

  4. 加载完成后使用requestAnimationFrame更新DOM

核心代码片段

const observer = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if(entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src; // 触发加载
      observer.unobserve(img);   // 停止观察
    }
  });
});

document.querySelectorAll('.lazy-img').forEach(img => {
  observer.observe(img); // 开始观察
});

本章重点总结

  1. 线程转移:用Web Workers处理CPU密集型任务
  2. 任务切片:通过分块执行保持主线程响应
  3. 事件调控:防抖节流控制高频触发
  4. 异步调度:合理使用宏任务/微任务
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_38220914

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值